Optimiser le chargement des images d'un site via un système de chargement différé (lazy loading)

Publié le 03 Mar 2021

Le choix des images est très important dans le design d’un site web. Elles doivent faire passer un message clair et être en cohésion avec le contenu textuel. Une fois la partie design validée, il faut pouvoir les intégrer correctement dans le site web et qu’elles s’affichent rapidement sur les navigateurs, pour ne pas venir perturber la lecture de l’internaute.

Sur de nombreux sites, on voit apparaître depuis quelques temps des systèmes de chargement différé des images. Ils permettent aux navigateurs de remplacer une image par un autre contenu (par exemple une image basique très légère comme un logo), le temps que l’image à intégrer soit chargée. Le but de ces systèmes est que les différents éléments du site apparaissent directement à la bonne place sans créer de décalages ou d’espaces vides dans le design du site. Ils permettent également d’améliorer les performances du site. Ainsi, même lors de son chargement, le site a tout de suite un rendu plus qualitatif et fini.

Au travers de cet article, nous vous proposons de créer un système de chargement différé des images grâce à Javascript.

Déclaration de l’image côté HTML

Pour commencer, on déclare comme d'habitude l'image à charger dans notre fichier HTML, en y intégrant quelques propriétés complémentaires :

  • src : qui contiendra un lien vers une image basique très légère qui se chargera très rapidement lors de l’affichage de la page dans le navigateur.
  • data-src : qui contiendra le lien vers l’image finale à charger sur la page.
  • class : utilisée pour styliser le rendu de l’image mais également pour appeler l’image côté Javascript.
  • width et height: pour indiquer aux navigateurs la taille que doit avoir l’emplacement de l’image.

        <img src="image.jpg" class="lazy" data-src="/images/full-image.jpg" width="390" height="300">
    

Le chargement différé des images côté Javascript

Nous utiliserons la fonction Intersection Observer de Javascript car elle offre la possibilité de détecter la visibilité d’un élément par rapport à un autre et donc de ne déclencher le système de chargement différé que lorsque l’image est présente sur la fenêtre du navigateur. Cela permettra d’optimiser le temps de chargement du site. Comme cette fonction en est encore au stade de développement et n’est donc pas supportée par tous les navigateurs, nous prévoirons aussi une action spécifique pour ces navigateurs.


        document.addEventListener("DOMContentLoaded", function () {
    let lazyloadImages;

    // Si le navigateur prend en compte la fonction Intersection Observer
    if ("IntersectionObserver" in window) {
        // On sélectionne toutes les images avec la class lazy présentes sur notre fenêtre
        lazyloadImages = document.querySelectorAll(".lazy");

        // On crée notre fonction
        let imageObserver = new IntersectionObserver(function (entries, observer) {
            entries.forEach(function (entry) {
                if (entry.isIntersecting) {
                    var image = entry.target;
                    // On modifie l’attribut « src » de l’image pour y mettre celui de l’image finale
                    image.src = image.dataset.src;
                    // On supprime la class « lazy » de l’image
                    image.classList.remove("lazy »);
                    // On stoppe l’observation de cet élément
                    imageObserver.unobserve(image);
                }
            });
        });

        // On lance notre fonction pour chacune des images
        lazyloadImages.forEach(function (image) {
            imageObserver.observe(image);
        });
    } else {
        // Si le navigateur ne prend pas en compte la fonction Intersection Observer
        let lazyloadThrottleTimeout;
        // On sélectionne toutes les images ayant la class lazy
        lazyloadImages = document.querySelectorAll(".lazy");

        // On crée la fonction de chargement différé des images
        function lazyload() {
            if (lazyloadThrottleTimeout) {
                clearTimeout(lazyloadThrottleTimeout);
            }

            lazyloadThrottleTimeout = setTimeout(function () {
                // On observe où en est la page en terme de scroll vertical
                let scrollTop = window.pageYOffset;
                lazyloadImages.forEach(function (img) {
                    if (img.offsetTop < (window.innerHeight + scrollTop)) {
                        // On modifie l’attribut « src » de l’image pour y mettre celui de l’image finale
                        img.src = img.dataset.src;
                        // On supprime la class « lazy » de l'élément
                        img.classList.remove('lazy');
                    }
                });
                if (lazyloadImages.length === 0) {
                    // S’il n’y a plus d’images on arrête de détecter le défilement vertical, le redimensionnement de la fenêtre et le changement d'orientation
                    document.removeEventListener("scroll", lazyload);
                    window.removeEventListener("resize", lazyload);
                    window.removeEventListener("orientationChange", lazyload);
                }
            }, 20);
        }

        // On lance la fonction en utilisant des event listeners pour contrôler l’apparition des images lors du défilement vertical, du redimensionnement de la fenêtre et du changement d'orientation
        document.addEventListener("scroll", lazyload);
        window.addEventListener("resize", lazyload);
        window.addEventListener("orientationChange", lazyload);
    }
});
    

Vous avez maintenant un système de chargement différé des images prêt à l’emploi ! C’est bien, mais qu’en est-il si on désactive le Javascript sur de notre navigateur ? Et bien les images finales ne se chargent plus… Pas de panique, nous avons la solution !

Il existe une balise HTML appelée <noscript> qui permet d’afficher des éléments lorsque le navigateur ne prend pas en charge Javascript ou s’il est désactivé. Il suffit donc d’ajouter cette balise dans notre fichier HTML et d’y déclarer notre image finale.


        <figure>
    <img src="image.jpg" class="img-basic lazy" data-src="/images/full-image.jpg" width="390" height="300">
    <noscript>
        <img src="/images/full-image.jpg" class="noscript-img" width="390" height="300">
    </noscript>
</figure>
    

Pour ne pas avoir un effet « double-image » lors de l’affichage de la page sans Javascript, on ajoute un peu de style sur les éléments que l’on vient de déclarer (chez Com to Code, on utilise Sass pour créer nos feuilles de style):


        figure {
  position: relative;
}

.img-basic {
  position: relative;
}

.no-script-img {
  position: absolute;
  top: 0;
  left: 0;
}
    

Ainsi, pour les navigateurs qui ne supportent pas Javascript ou sur lesquels il a été désactivé, la seconde image, placée dans la balise <noscript> viendra se placer au dessus de l’autre image ne comportant que l’image basique.

Aller un peu plus loin dans le style des images

Maintenant que notre système de chargement différé est en place, on peut se dire qu’il serait plus agréable visuellement d’avoir une animation sur les images qui sont pré-chargées sur la page. Ajoutons donc un peu de style pour créer une transition sur l'opacité de l'image.

Sass :


        .img-basic {
  position: relative;
  height: auto;
  opacity: 1;
  transition: opacity 0.5s ease;
  &.lazy {
    opacity: 0.01;
  }
}
    

Rechargez votre page et voyez vos images apparaître avec un joli effet d’apparition !

On peut aussi remplacer l’image par un SVG, qui est un format d'image léger et très rapide à charger par les navigateurs. On pourrait placer les informations sur la taille de l’image également directement dans notre SVG pour qu’il prenne bien la taille de l’emplacement de l’image.