Qu'est-ce que le Lazy Loading, et à quoi ça sert ?
Le Lazy Loading (qu'on pourrait traduire par "Chargement fainéant") consiste à ne charger un élément (en général une image) que lorsque c'est nécessaire. C'est à dire que si l'élément n'est pas visible, parce que plus bas sur la page par exemple, il ne sera pas chargé en même temps que la page mais seulement lorsqu'il deviendra visible. On parle de chargement différé.
Comme tous les éléments "hors champ" ne sont pas chargés, ce mécanisme a un impact bénéfique dans la vitesse de chargement d'une page. Et donc de l'expérience utilisateur. C'est également bénéfique en termes de nombre de requêtes et de bande passante, et donc plus écologique car les serveurs travaillent moins, et plus économique sur la consommation des données mobiles notamment. C'est particulièrement apprécié quand une page contient énormément d'images, comme par exemple une galerie ou un catalogue e-commerce. En théorie si on consulte l'ensemble de la page ça ne fait pas de différence qu'il y ait du lazy loading ou non, mais il arrive souvent qu'on arrive sur une page et qu'on reparte sans l'avoir consultée entièrement.
Les côtés négatifs du lazy loading
Malheureusement, le concept ne vient pas sans son lot de points négatifs :
- Lorsqu'il n'est pas bien fait, il peut nuire au référencement. Je pense notamment aux systèmes qui bloquent la création des balises <img>, ou encore les "scroll infini", qui chargent la suite d'une page en Ajax une fois le bas de page atteint. Comme ces contenus n'existent pas dans le code source les robots des moteurs de recherche ne peuvent pas les prendre en compte dans leur analyse du contenu. Sans parler que de base un scroll infini n'est pas idéal en termes d'expérience utilisateur, car il ne sait pas où il en est rendu dans la lecture totale de la page.
- Une image ne se charge pas instantanément, le navigateur doit dans un premier temps la télécharger avant de l'afficher. Comme le principe du lazy loading consiste à afficher une image quand on en a besoin, il peut arriver qu'on voit l'image charger. Ce n'est pas un problème bien grave, mais ça peut frustrer ceux qui recherchent une expérience utilisateur irréprochable. En général, ce problème est réglé en chargeant l'image non pas quand elle atteint le niveau de scroll où elle devient visible, mais quelques centaines de pixels plus bas. Ce n'est pas une garantie qu'il n'y ait pas de chargement visible, car beaucoup de paramètres entrent en jeu : vitesse du scroll, poids de l'image, débit de la connexion... mais ça peut aider. Une autre solution, quand le design le permet, peut être d'afficher l'image avec une animation, un fondu par exemple, une fois que celle-ci est chargée : ainsi l'effet devient volontaire et maîtrisé.
- Jusqu'à fin 2019 (vous vous doutez bien en lisant le titre de l'article que ce n'est plus une nécessité), il fallait obligatoirement passer par du javascript pour mettre ce système en place. Le javascript ce n'est pas le mal, mais ça fait encore des éléments supplémentaires à charger, il faut prendre du temps pour le développer quand on veut du sur-mesure, ou alors parfois utiliser des grosses bibliothèques dont on ne se sert que d'une partie. Je suis sûr qu'il existe un bon nombre de pages où les gains apportés ne par le lazy load ne valent pas le coût en ressources engendré par sa mise en place.
Le lazy loading sans javascript
Les navigateurs utilisant Chromium (Chrome, Edge, Opera) et Firefox peuvent depuis quelques mois gérer le lazy loading des images et des iframes de façon native, sans javascript, seulement avec un nouvel attribut HTML : loading
. À l'heure ou j'écris cet article, ce n'est pas encore le cas sur Safari soit environ 20% des internautes français, dont 17% sur mobile là où le lazy load a davantage d'importance, ce qui n'est pas négligeable. Vous pouvez aller jeter un œil sur caniuse.com pour suivre l'évolution des disponibilités et des statistiques.
Comment ça marche ?
L'utilisation du lazy loading natif est très simple. Voire même trop simple, car aucun réglage comme la hauteur après la partie cachée à partir de laquelle n'est possible. Comme je vous le disais dans le paragraphe précédent, il suffit d'ajouter un attribut loading
sur une balise <img>
(y compris dans <picture>
) ou <iframe>
. Les valeurs possibles sont les suivantes :
- lazy : ne charge pas l'élément tant que celui-ci n'atteint pas le "seuil" à partir duquel le navigateur estime qu'il faut la charger. Initialement, ce seuil était très grand (3000px pour les connexions en 4G, 4000px pour les connexions plus lentes), le mois dernier (juillet 2020) ces valeurs ont été revues à la baisse (respectivement 1250px et 2500px) pour se rapprocher davantage de ce qui se trouve généralement sur les versions javascript (ces valeurs restent toutefois assez grandes).
- eager : charge l'élément immédiatement, qu'il soit hors viewport ou non.
- sur Chromium, il existe également la valeur auto : même effet que de ne pas mettre d'attribut loading. Par défaut, Chromium établit une priorité dans le chargement des images, où celles qui sont en-dehors du viewport sont chargées avec une priorité moindre (mais quand même chargées dès que possible). Chrome propose également la fonctionnalité "lite mode", qui entre autres appliquera un lazy load sur les éléments par défaut. Cette valeur ne fait pas partie des spécifications, elle pourrait donc changer un jour, mieux vaut donc éviter de l'utiliser.
Quelques exemples
Sur une image classique :
Quand c'est possible, il est recommandé de définir les dimensions d'image à l'aide des attributs width et height, ou style. En effet, lorsqu'ils ne sont pas présents le navigateur ne sait pas quelle taille font les images tant qu'elles ne sont pas chargées, ce qui peut créer des décalages de structure au moment de leur chargement.
<img src="/chemin/mon-image.jpg" alt="Un exemple" width="800" height="600" loading="lazy">
Sur un élément <picture> :
Le principe de l'élément <picture> est de spécifier plusieurs sources d'image en fonction du contexte, avec une ou plusieurs balises <source> et une balise <img>. Dans le cas d'un élément <picture>, il n'y a qu'un seul attribut loading à mettre, quel que soit le nombre de <source>, et il est à mettre sur la balise <img>.
<picture> <source srcset="/chemin/mon-image-S.jpg" media="(max-width: 340px)"> <source srcset="/chemin/mon-image-M.jpg" media="(max-width: 1260px)"> <source srcset="/chemin/mon-image-L.jpg"> <img src="/chemin/mon-image.jpg" alt="Un exemple" loading="lazy"> </picture>
Sur un élément <iframe> :
Les iframes sont beaucoup utilisées pour inclure des services tiers sur un site (plateformes vidéo, réseaux sociaux, analyses et statistiques, etc.). Voici un exemple pour une vidéo Youtube, le principe est le même que pour une balise <img>.
<iframe src="https://www.youtube-nocookie.com/embed/tzVJPgCn-Z8" loading="lazy" width="560" height="315" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
NB : il est possible qu'à terme Youtube ajoute loading="lazy" par défaut sur ses iframes, une note interne a été ouverte en ce sens.
Sur Chrome, certaines iframes sont considérées comme cachées (en général celles qui sont utilisées par les services d'analyses et de statistiques), et celles-ci ne sont pas différées dans la plupart des cas, qu'elles soient en loading="lazy" ou non. Chrome considère qu'une iframe est cachée si au moins une des conditions suivantes est remplie :
- sa largeur et sa hauteur font 4px ou moins,
- un
display:none
ou unvisibility:hidden
est appliqué - l'iframe est placée hors écran avec un positionnement X ou Y négatif
Des questions ?
Comment sont chargées les images qui sont dans le viewport mais non visibles (carrousel, cachées en CSS...) ?
La version native de cette fonctionnalité ne se base que sur la position des images par rapport au viewport. Une image de carrousel sera donc chargée dès qu'elle se trouve dans la zone du viewport, même si elle n'est pas visible.
Est-ce que le lazy loading natif marche aussi sur les background-image en CSS ?
Non, ça ne fonctionne que sur les balises <img> et <iframe>.
Est-ce qu'on peut changer la distance du viewport à partir de laquelle les images en lazy loading natif sont chargées ?
Non, ces valeurs sont définies en dur dans le code source de Chromium. Les développeurs tendent à s'inspirer de ce qui se trouve dans les scripts javascript de lazy loading, mais semblent assez frileux à mettre des valeurs assez basses.
Comment ça se passe si j'utilise déjà un script javascript qui gère le lazy loading ?
Cela dépend du fonctionnement du script utilisé, mais en général il n'y a pas vraiment d'impact dans le sens où les deux se complètent. La principale différence se joue sur la distance après la partie cachée du viewport. Si celle du script est plus grande que la valeur native, alors le script rend disponible l'image mais celle-ci est toujours bloquée par le fonctionnement natif. A l'inverse, si la valeur du script est plus petite, le chargement de l'image natif tente de se faire mais est bloqué par le script. Pour résumer, c'est le fonctionnement avec la valeur de seuil la plus proche du bord du viewport qui détermine quand l'image charge.
Est-ce qu'il y a moyen de détecter si le navigateur peut utiliser le lazy load de façon native ?
Oui, en javascript on peut faire ainsi :
if( 'loading' in HTMLImageElement.prototype ){ // le navigateur peut gérer le lazy loading natif }else{ // le navigateur ne connaît pas cette fonctionnalité, on peut charger un script tiers }
Pour conclure
Comme on a pu le voir, le lazy loading natif permet de s'économiser des scripts supplémentaires, mais il n'est pas exempt de défauts. Parmi ces défauts, on retiendra le fonctionnement basique : appliqué seulement aux images et iframes, dans un contexte relatif à la présence ou non dans ou à proximité du viewport. Gros défaut également, l'impossibilité de régler la valeur de la distance au-delà du viewport à partir de laquelle l'élément sera chargé. Par contre, au-delà du gain de script que ça apporte, le lazy load natif ne se base pas sur le niveau de scroll de la page. C'est à dire que si on se rend directement à la fin d'une page, les images de la fin seront chargées mais pas celles du début, alors que les scripts basés sur le scroll afficheraient l'ensemble des images, du début à la fin.
Cette fonctionnalité native est encore trop fraîche pour être parfaite. Étant donné sa valeur de seuil élevée, les scripts javascripts sont encore aujourd'hui souvent plus efficaces en termes de réduction de données chargées. Mais moi qui aime à faire du web simple, sans s'encombrer de scripts à tout va, je vois ça comme une avancée positive, et je vais suivre l'évolution de cette fonctionnalité en espérant que ça devienne vraiment intéressant.
Et vous, qu'en pensez-vous ? Utilisez-vous le lazy loading natif pour vos projets ? N'hésitez pas à participer à cet article, cet espace est pour vous !
Mais il faut bien un début à tout, pas vrai ?