Le chargement paresseux ou lazy loading

Et ce n’est pas un vice.

Le chargement paresseux permet de ne charger une ressource que quand elle est nécessaire et peut donc contribuer à diminuer la consommation d’énergie et augmenter la performance d’une application. Il ne faut pas confondre cette technique avec le chargement différé où le chargement est retardé, mais quand même réalisé.

Cette technique est utilisable pour tous les types d’applications, par exemple développées avec des langages comme Swift, Kotlin, Java, Rust, C++, etc. mais nous ne parlerons ici que du développement Web côté client en faisant un tour d’horizon des différentes possibilités qui existent au niveau HTML, CSS et JavaScript pour faire du chargement paresseux et du chargement dynamique de ressources ou de scripts.

HTML

Le chargement paresseux peut aujourd’hui être utilisé avec 2 éléments HTML, l’élément <img> et l’élément <iframe>.

Élément <img>

Utiliser le chargement paresseux d’une image est extrêmement simple, il suffit d’ajouter l’attribut loading avec la valeur lazy à l’élément <img>. La valeur eager de cet attribut est la valeur par défaut, l’image est chargée immédiatement.

Exemple :

<img loading="lazy" src="/img/greenersoft.png" alt="GreenerSoft Logo">

Avec cet attribut placé sur un élément <img>, l’image référencée n’est chargée par le navigateur que quand elle devient potentiellement visible à l’écran. Donc, les images placées sous la ligne de flottaison, ne sont pas chargées quand un utilisateur arrive sur une page, c’est en faisant défiler la page qu’elles le sont au fur et à mesure qu’elles deviennent visibles.

Les images avec du chargement paresseux ne sont jamais chargées si elles n’ont pas d’intersection avec une partie visible d’un élément, il faut donner une dimension aux images avec les attributs width et height ou par CSS pour régler ce problème. Elles sont par contre systématiquement chargées si JavaScript est désactivé dans le navigateur pour empêcher le pistage.

Cette possibilité fonctionne dans tous les navigateurs depuis plusieurs années, le dernier l’ayant intégrée étant Safari version 15.4 en 2022. Il n’est donc absolument pas nécessaire (c’est même contreproductif) d’ajouter du code JavaScript pour le faire. Et les navigateurs anciens qui ne supportent pas l’attribut l’ignorent.

Élément <iframe>

Pour les éléments <iframe>, utiliser le chargement paresseux est aussi simple que pour les images, il suffit là aussi d’ajouter l’attribut loading avec la valeur lazy. Et comme pour les images, la valeur eager de cet attribut est la valeur par défaut, l’iframe est immédiatement chargé.

Exemple :

<iframe loading="lazy" src="/iframe.html"></iframe>

Comme pour les images, l’iframe n’est chargé par le navigateur, que quand il devient potentiellement visible.

Ceci est particulièrement intéressant quand l’iframe est un lecteur de vidéo externe, comme par exemple avec YouTube, où même sans démarrage automatique de la vidéo (mauvaise pratique 😡), plusieurs mégaoctets de données sont automatiquement téléchargés.

YouTube ne met pas cet attribut dans ses codes d’intégration et c’est bien dommage, n’hésitez donc pas à l’ajouter vous même avant d’intégrer un code dans vos pages HTML.

Tous les navigateurs supportent aujourd’hui cet attribut, le dernier l’ayant intégré étant Safari version 16.4 en 2023.

CSS

Il n’existe malheureusement pas de solution de base au niveau des CSS pour le chargement paresseux des images qui sont en arrière-plan d’un élément (background-image), une propriété qui pourrait s’appeler background-image-loading prenant la valeur lazy, n’existe pas en CSS.

L’idée a été soumise par Addy Osmani (ingénieur logiciel travaillant actuellement sur Google Chrome) en 2021 sur le GitHub du WHATWG, espérons qu’elle sera retenue un jour par le W3C.

N’hésitez-pas à mettre un pouce 👍 à l’idée sur GitHub, cela aidera peut être le W3C, qui parle depuis peu de conception Web durable (Web Sustainability Guidelines (WSG) 1.0), à passer aux actes et à normaliser cet idée qui n’est ensuite probablement pas bien compliquée à mettre en œuvre dans les navigateurs, le gros du travail étant déjà fait via la prise en compte du chargement paresseux pour les éléments <img>.

En attendant, il est toujours possible de faire cela en JavaScript, mais le remède est probablement pire que le mal, nous passerons donc notre chemin.

Javascript

Avec JavaScript nous parlerons plus de chargement dynamique que de chargement paresseux, c’est un peu la même chose, mais c’est vous, en tant que développeur éco-responsable, qui décidez quand réaliser un chargement.

Il est depuis très longtemps possible de faire du chargement différé de fichiers JavaScript avec les attributs booléens async ou defer placés sur les éléments <script>, mais il s’agit d’un chargement retardé qui arrivera à un moment ou à un autre, ce n’est donc pas un chargement paresseux qui lui intervient quand on a besoin de la ressource et donc éventuellement pas.

Cette précision faite, il est néanmoins grandement conseillé d’utiliser ces attributs car cela évite de bloquer le navigateur pendant sa phase d’interprétation du code HTML et réduit donc en général le temps d’affichage d’une page.

JavaScript permet par ailleurs de charger dynamiquement d’autres types de ressources (HTML, JSON, etc.) en utilisant la méthode fetch() quand le besoin est là.

Les scripts Javascript

Pour un script JavaScript ordinaire, il est possible de le charger dynamiquement en ajoutant un élément <script> à la page, typiquement en utilisant la méthode createElement() pour créer l’élément <script> et la méthode append() pour l’ajouter dans le DOM de la page.

Cette technique est souvent utilisée pour charger une API externe quand nous en avons besoin et donc pas systématiquement au chargement d’une page.

Les modules Javascript

Comme nous l’indiquons dans un article précédent, les modules JavaScript (modules ECMAScript ou ESM) peuvent être chargés dynamiquement en utilisant la fonction import().

Exemple :

try {
let Tools = await import("Tools");
Tools.showMessage("Hello World");
} catch (e) {
// Gérer ici l'erreur d'importation
}

C’est la façon la plus simple pour faire un chargement dynamique de vos fichiers JavaScript, pas besoin de polluer le DOM de vos pages en y ajoutant des éléments <script> comme présenté ci-avant. Et la fonction retourne une promesse, il est donc facile de gérer d’éventuelles erreurs.

Nous recommandons cette méthode pour le chargement dynamique de fichiers JavaScript en utilisant non pas une URL (relative ou absolue), mais le nom du module comme paramètre de la fonction import() en liaison avec une carte d’importation (plus d’informations dans notre article sur les modules JavaScript).

L’API Intersection Observer

L’API Intersection Observer permet de détecter le changement d’état de la visibilité d’un élément du DOM d’une page par rapport à son ancêtre et de déclencher des actions sur ce changement.

Elle donne donc la possibilité de réaliser des actions pour ses propres besoins comme fait le navigateur automatiquement avec l’attribut loading et la valeur lazy des éléments <script> ou <iframe>.

L’action peut être un chargement d’une ressource ou même d’un module JavaScript en gardant en tête que l’action doit être rapide car exécutée dans le thread principal et peut donc amener des blocages de l’interface utilisateur si son temps d’exécution est trop long.

Cette API est aujourd’hui supportée par tous les navigateurs (avec une petite limitation pour Firefox et Safari), c’est donc le bon outil pour réaliser du chargement paresseux personnalisé.

Les Web Workers

Dans les Web Workers, la solution avec l’ajout d’un élément <script> pour charger un script supplémentaire, n’est pas possible, les Web Workers n’ayant pas accès au DOM des pages.

Il existe donc une solution officiel qui est la méthode importScripts(). Elle permet de dynamiquement charger et exécuter un ou plusieurs scripts à la fois suivant vos besoins.

Pour le chargement d’autres ressources, la méthode fetch() est bien sûr utilisable dans les Web Workers comme dans un script JavaScript ordinaire.

Conclusion

Le chargement paresseux est très simple à mettre en œuvre au niveau de HTML pour les images et les iframes et sans utiliser de JavaScript, il est même dommage qu’il ne soit pas réalisé par défaut par les navigateurs.

Pour les fichiers JavaScript, l’utilisation de modules ECMAScript est une solution idéale car vraiment simple à mettre en œuvre.

Au final, charger paresseusement et dynamiquement des ressources et non pas charger la totalité à l’arrivée sur une page, améliore l’expérience utilisateur car moins de données sont transférées et ce qui implique souvent un affichage plus rapide.

C’est donc bon pour le référencement de votre site, bon pour l’utilisateur qui consomme moins de bande passante et d’énergie, bon pour vos serveurs qui sont moins sollicités et donc consomment aussi moins d’énergie.

Bref, c’est une bonne pratique pour l’éco-conception Web.