logo oujood
🔍

Intersection Observer API JavaScript

L'Intersection Observer API permet de détecter quand un élément HTML entre ou sort de la zone visible du navigateur (le viewport), sans écouter l'événement scroll. Une approche performante qui remplace les calculs manuels de position coûteux.

OUJOOD.COM

Avant l'Intersection Observer, détecter qu'un élément était visible à l'écran passait par l'événement scroll combiné à getBoundingClientRect(). Ce calcul s'exécutait à chaque pixel de défilement, forçait le navigateur à recalculer la mise en page, et plombait les performances sur les pages longues.

L'Intersection Observer API inverse la logique : on déclare les éléments à surveiller et le navigateur appelle une fonction de rappel uniquement quand leur visibilité change. Pas de boucle, pas de calcul manuel — le navigateur optimise tout en interne.

Créer un observer de base

On instancie un IntersectionObserver avec une fonction de rappel, puis on lui confie les éléments à surveiller avec observe() :

Exemple :   📋 Copier le code

// Créer un observer
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      // L'élément est entré dans le viewport
      console.log('Visible :', entry.target.id);
    } else {
      // L'élément est sorti du viewport
      console.log('Caché :', entry.target.id);
    }
  });
});

// Surveiller un ou plusieurs éléments
observer.observe(document.getElementById('section1'));
observer.observe(document.getElementById('section2'));

Les propriétés d'une entrée

La fonction de rappel reçoit un tableau d'objets IntersectionObserverEntry. Chacun expose plusieurs propriétés utiles :

Exemple :   📋 Copier le code

const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    console.log(entry.target);          // l'élément observé
    console.log(entry.isIntersecting);  // true si visible dans le viewport
    console.log(entry.intersectionRatio); // 0 à 1 — fraction visible
    console.log(entry.boundingClientRect); // position de l'élément
    console.log(entry.intersectionRect);   // zone d'intersection
  });
});

Options de configuration

Le deuxième argument du constructeur permet d'affiner le déclenchement avec trois options :

Exemple :   📋 Copier le code

const options = {
  root: null,          // null = viewport du navigateur
  rootMargin: '0px',   // marges autour du root (comme du CSS)
  threshold: 0.5       // déclencher quand 50% de l'élément est visible
};

const observer = new IntersectionObserver(callback, options);

// threshold peut être un tableau pour plusieurs seuils
const observerMultiSeuils = new IntersectionObserver(callback, {
  threshold: [0, 0.25, 0.5, 0.75, 1]
  // rappel déclenché à 0%, 25%, 50%, 75% et 100% de visibilité
});

Lazy loading d'images

Le cas d'usage le plus répandu : ne charger les images que quand elles approchent du viewport. On stocke l'URL réelle dans un attribut data-src et on la bascule dans src au bon moment :

Exemple :   📋 Copier le code

const observerImages = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (!entry.isIntersecting) return;

    const img = entry.target;
    // Charger l'image en basculant data-src vers src
    img.src = img.dataset.src;
    img.classList.add('chargee');

    // Arrêter d'observer une fois l'image chargée
    observerImages.unobserve(img);
  });
}, {
  rootMargin: '200px' // commencer à charger 200px avant l'entrée dans le viewport
});

// Observer toutes les images avec data-src
document.querySelectorAll('img[data-src]').forEach((img) => {
  observerImages.observe(img);
});

Animations déclenchées au scroll

On peut animer des éléments à leur entrée dans le viewport en ajoutant une classe CSS au bon moment :

Exemple :   📋 Copier le code

/* CSS — état initial et animation */
/*
.apparaitre {
  opacity: 0;
  transform: translateY(30px);
  transition: opacity 0.6s ease, transform 0.6s ease;
}
.apparaitre.visible {
  opacity: 1;
  transform: translateY(0);
}
*/

// JavaScript — déclencher l'animation à l'entrée dans le viewport
const observerAnim = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.classList.add('visible');
      // Arrêter d'observer — l'animation ne se joue qu'une fois
      observerAnim.unobserve(entry.target);
    }
  });
}, { threshold: 0.15 });

document.querySelectorAll('.apparaitre').forEach((el) => {
  observerAnim.observe(el);
});

Infinite scroll

On place un élément sentinelle en bas de liste. Quand il entre dans le viewport, on charge la page suivante de contenu :

Exemple :   📋 Copier le code

let pageActuelle = 1;
let chargementEnCours = false;
const sentinelle = document.getElementById('sentinelle');

const observerScroll = new IntersectionObserver(async (entries) => {
  if (!entries[0].isIntersecting || chargementEnCours) return;

  chargementEnCours = true;
  pageActuelle++;

  // Charger la page suivante
  const data = await fetch(`/api/articles?page=${pageActuelle}`)
    .then(r => r.json());

  ajouterArticles(data.articles);
  chargementEnCours = false;

  // Arrêter si on a atteint la dernière page
  if (!data.pagesSuivantes) {
    observerScroll.unobserve(sentinelle);
  }
});

observerScroll.observe(sentinelle);

Suivi des sections actives dans la navigation

On peut mettre en surbrillance le lien de navigation correspondant à la section actuellement visible — utile pour les pages longues avec un sommaire fixe :

Exemple :   📋 Copier le code

const observerNav = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    const id = entry.target.id;
    const lien = document.querySelector(`nav a[href="#${id}"]`);
    if (!lien) return;

    if (entry.isIntersecting) {
      // Marquer le lien comme actif
      document.querySelectorAll('nav a').forEach(a => a.classList.remove('actif'));
      lien.classList.add('actif');
    }
  });
}, { threshold: 0.5 });

// Observer toutes les sections de la page
document.querySelectorAll('section[id]').forEach((section) => {
  observerNav.observe(section);
});

Arrêter et déconnecter un observer

Pour libérer les ressources quand un observer n'est plus utile :

Exemple :   📋 Copier le code

// Arrêter de surveiller un élément précis
observer.unobserve(monElement);

// Déconnecter l'observer complètement (tous les éléments)
observer.disconnect();

Par carabde | Mis à jour le 17 avril 2026


chapitre précédent   sommaire   chapitre suivant