IntersectionObserver - une aide au lazy loading

Depuis la norme HTML 5.2, une nouvelle notion est apparue dans le moteur JavaScript, nous permettant à la fois d'améliorer le lalzy loading de nos applications et de faire un pas vers les progressives Web App: l'api IntersectionObserver.

Le but de cet api: observer un ensemble d'élements et savoir s'ils sont visibles. Autrement dit, s'assurer que l'image est visible par l'oeil de l'utilisateur, aussi bien dans le viewport de notre navigateur que dans un élément de notre page.



Prenons le cas suivant:


C'est un cas très classique, où nous aimerions charger que les images qui ont besoin d'être vue. En effet, pour chaque image, un appel HTTP en "GET", sollicitant à la fois le serveur, mais surtout consommant de la bande passante (pas top dans un contexte mobile) et aussi sollicitant le navigateur.

En effet, ce dernier (jusqu'à HTTP2) n'arrive à gérer que 5 appels HTTP sur son domaine. Du coup, si vous avez des requêtes Ajax qui sont faites en même temps, ou que vous essayez de charger d'autres ressources, la navigateur va devoir traiter un grand nombre d'appels et donc retarder l'affichage ou le traitement. Et tout cela car nous voulons charger un grand nombre d'images.

IntersectionObserver va nous permettre alors de gérer cela de manière efficace.

Tout d'abord, nos images ne devront pas avoir directement l'attribut "src", car c'est lui qui fait l'appel HTTP:

<img class="main__gallery__item"
data-src="http://www.petmd.com/sites/default/files/scared-kitten-shutterstock_191443322.jpg">


Maintenant, nous allons faire un petit code qui:
  1. Va détecter les images
  2. Les observer
  3. A chaque déplacement, analyser l'image afin de déterminer si cela faut le coup de l'afficher
  4. Si toutes les images ont été affichées, arrêter l'observation

Ce qui donne:


'use strict';

function hasDataSrcAttribute(htmlElement) {
 return !!htmlElement.getAttribute('data-src');
}

// Remember the list of images
const images = document.querySelectorAll('.main__gallery__item');

// Put in place the observation
let observer = new IntersectionObserver(
 entries => {
   let hasDataSrc = entries.find(entry => hasDataSrcAttribute(entry.target));
    
    if (hasDataSrc) {
     entries
       .filter(entry => hasDataSrcAttribute(entry.target))
        .forEach(function (entry) {
         if (entry.intersectionRatio > 0) {
            // We have to load the image, so no need anymore to observe it
            observer.unobserve(entry.target);
            
            // Load the image
            entry.target.src = entry.target.getAttribute('data-src');
            entry.target.removeAttribute('data-src');
          }
        });
    
    } else {
     // No need anymore the observer: all the images were loaded
      observer.disconnect();
    }
  },
  {
   // If the image gets within 50px in the Y axis, start the download.
   'rootMargin': '50px 0px',
   'threshold': 0.01
  }
);

// Associated the observer on each images
images.forEach(image => observer.observe(image));


Et nous avons le résultat final suivant: https://jsfiddle.net/rochejul/307zbzho/1/

Cela ne saute pas aux yeux, car nous avons configurer l'observateur de sorte que dès que l'image s'approche de la zone, nous faisions un préchargement.

Voici un autre exemple où nous chargeons l'image uniquement quand celle-ci est présent à 50% dans la zone: https://jsfiddle.net/rochejul/r0z1tqop/2/


Nous pouvons d'autres cas d'usages, comme par exemple faire un scroll infini: https://googlechrome.github.io/samples/intersectionobserver/index.html

Bref, bien utile.





Commentaires

Posts les plus consultés de ce blog

ISO: liens & outils utiles

NodeJs et SSL: une petite analyse

Créer sa commande slack en quelques minutes