// apis · Web Platform Advent #10
L'API IntersectionObserver : lazy-loading et effets au scroll
Utilisez IntersectionObserver pour charger les images en différé, révéler des éléments au scroll et créer un défilement infini — threshold et rootMargin expliqués, avec des exemples exécutables.
L'API IntersectionObserver vous indique quand un élément entre ou sort de la fenêtre d'affichage — sans avoir à brancher un écouteur scroll et à recalculer des positions à chaque image. Le navigateur effectue la surveillance de façon asynchrone et hors du thread principal, ce qui reste fluide même avec des centaines de cibles. Tous les navigateurs actuels la prennent en charge.
Pourquoi ne pas simplement écouter le scroll ?
Un gestionnaire scroll se déclenche en permanence et vous oblige à appeler getBoundingClientRect() pour savoir où se trouvent les éléments — ce qui lit le layout et peut provoquer des saccades. IntersectionObserver inverse le modèle : vous enregistrez les éléments qui vous intéressent, et le navigateur ne vous prévient que lorsque leur visibilité change réellement.
Le schéma de base
Créez un observateur avec une fonction de rappel, puis appelez observe() sur un ou plusieurs éléments. Le rappel reçoit un tableau d'objets entry ; la propriété clé est entry.isIntersecting :
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
console.log('Visible maintenant :', entry.target);
}
}
});
document.querySelectorAll('.watch').forEach((el) => observer.observe(el)); Charger les images en différé (lazy-loading)
Un usage classique : ne charger une image que lorsqu'elle est sur le point d'apparaître. Stockez l'URL réelle dans data-src, injectez-la quand l'élément intersecte, puis cessez de l'observer :
const lazyImages = document.querySelectorAll('img[data-src]');
const imgObserver = new IntersectionObserver((entries, observer) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img); // charger une fois, puis arrêter
}
}, { rootMargin: '200px' });
lazyImages.forEach((img) => imgObserver.observe(img)); À noter : le lazy-loading natif via <img loading="lazy"> couvre désormais la plupart des cas. Utilisez IntersectionObserver quand vous avez besoin de logique sur mesure — placeholders, fondus, ou chargement de ressources autres que des images.
Révéler des éléments au scroll
Ajoutez une classe CSS lorsqu'un élément devient visible pour la première fois, afin qu'il puisse s'animer. L'option threshold décide quelle part de l'élément doit être visible avant le déclenchement :
const revealObserver = new IntersectionObserver((entries, observer) => {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
}
}
}, { threshold: 0.2 }); // déclenche quand 20% est à l'écran
document.querySelectorAll('.reveal').forEach((el) => revealObserver.observe(el)); Défilement infini
Placez un élément sentinelle vide après votre liste. Quand il entre dans la fenêtre, récupérez et ajoutez la page suivante, puis continuez à observer pour la suivante :
const sentinel = document.querySelector('#sentinel');
let page = 1;
const loader = new IntersectionObserver(async (entries) => {
if (!entries[0].isIntersecting) return;
const items = await fetchPage(page++);
appendItems(items);
});
loader.observe(sentinel); Les options : threshold et rootMargin
Deux options déterminent le moment du déclenchement :
threshold— un nombre (ou un tableau) de 0 à 1.0déclenche dès qu'un pixel est visible ;1uniquement quand tout l'élément l'est. Un tableau comme[0, 0.5, 1]déclenche à chaque palier, pratique pour les effets de progression au scroll.rootMargin— une marge à la CSS qui agrandit ou réduit la zone de test.'200px'déclenche 200px avant que l'élément n'atteigne la fenêtre — idéal pour précharger.root— l'élément servant de fenêtre. Par défaut, la fenêtre du navigateur ; indiquez un conteneur défilable pour observer à l'intérieur de celui-ci.
Nettoyage
Cessez de surveiller un seul élément avec observer.unobserve(el), ou démantelez tout avec observer.disconnect() — important dans les applications monopage lorsqu'une vue est démontée, pour éviter les fuites.
Aide-mémoire
| Objectif | Option / appel clé |
|---|---|
| Détecter la visibilité | entry.isIntersecting |
| Précharger en avance | rootMargin: '200px' |
| Attendre X% visible | threshold: 0.5 |
| Observer dans un conteneur | root: containerEl |
| Arrêter pour un élément | observer.unobserve(el) |
| Tout démanteler | observer.disconnect() |
IntersectionObserver remplace toute une catégorie de calculs de scroll fragiles par une petite API déclarative. Images en différé, révélations au scroll et listes infinies se ramènent au même schéma : observer, réagir à isIntersecting, puis cesser d'observer quand c'est fini.