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