// apis · Web Platform Advent #10
L'API IntersectionObserver: lazy-loading ed effetti allo scroll
Usa IntersectionObserver per caricare immagini in lazy-loading, rivelare elementi durante lo scroll e creare lo scroll infinito — con threshold e rootMargin spiegati, più esempi eseguibili.
L'API IntersectionObserver ti dice quando un elemento entra o esce dal viewport — senza che tu debba collegare un listener scroll e calcolare le posizioni a ogni frame. Il browser esegue il controllo in modo asincrono e fuori dal thread principale, quindi tutto resta fluido anche con centinaia di elementi osservati. Ogni browser attuale la supporta.
Perché non ascoltare semplicemente lo scroll?
Un handler scroll scatta di continuo e ti costringe a chiamare getBoundingClientRect() per sapere dove si trovano gli elementi — questo legge il layout e può causare scatti. IntersectionObserver ribalta il modello: registri gli elementi che ti interessano e il browser ti avvisa solo quando la loro visibilità cambia davvero.
Lo schema di base
Crea un observer con una callback, poi chiama observe() su uno o più elementi. La callback riceve un array di oggetti entry; la proprietà chiave è entry.isIntersecting:
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
console.log('Now visible:', entry.target);
}
}
});
document.querySelectorAll('.watch').forEach((el) => observer.observe(el)); Lazy-loading delle immagini
Un uso classico: caricare un'immagine solo quando sta per entrare nella vista. Memorizza l'URL reale in data-src, scambialo quando l'elemento interseca, poi smetti di osservarlo:
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); // load once, then stop watching
}
}, { rootMargin: '200px' });
lazyImages.forEach((img) => imgObserver.observe(img)); Nota: il lazy-loading nativo tramite <img loading="lazy"> copre ormai la maggior parte dei casi. Ricorri a IntersectionObserver quando ti serve una logica personalizzata — segnaposto, dissolvenze o il caricamento di risorse non immagine.
Rivelare elementi durante lo scroll
Aggiungi una classe CSS quando un elemento diventa visibile per la prima volta, così può animarsi all'ingresso. Usa l'opzione threshold per decidere quanto dell'elemento deve essere visibile prima che la callback scatti:
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 }); // fire when 20% is on screen
document.querySelectorAll('.reveal').forEach((el) => revealObserver.observe(el)); Scroll infinito
Posiziona un elemento sentinella vuoto dopo la tua lista. Quando entra nel viewport, recupera e accoda la pagina successiva, poi continua a osservare per la pagina seguente:
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); Le opzioni: threshold e rootMargin
Due opzioni determinano quando scatta la callback:
threshold— un numero (o array) da 0 a 1.0scatta nel momento in cui un pixel è visibile;1scatta solo quando l'intero elemento è visibile. Un array come[0, 0.5, 1]scatta a ogni passo, comodo per gli effetti di avanzamento dello scroll.rootMargin— un margine in stile CSS che ingrandisce o riduce l'area usata per il test.'200px'si attiva 200px prima che l'elemento raggiunga il viewport — ideale per il pre-caricamento.root— l'elemento da usare come viewport. Per impostazione predefinita è il viewport del browser; impostalo su un contenitore scorrevole per osservare all'interno di esso.
Pulizia
Smetti di osservare un singolo elemento con observer.unobserve(el), oppure smonta tutto con observer.disconnect() — importante nelle single-page app quando una vista viene smontata, per evitare perdite di memoria.
Riferimento rapido
| Obiettivo | Opzione / chiamata chiave |
|---|---|
| Rilevare la visibilità | entry.isIntersecting |
| Pre-caricare in anticipo | rootMargin: '200px' |
| Attendere finché X% è visibile | threshold: 0.5 |
| Osservare dentro un contenitore | root: containerEl |
| Fermarne uno | observer.unobserve(el) |
| Smontare tutto | observer.disconnect() |
IntersectionObserver sostituisce un'intera categoria di fragili calcoli di scroll con una piccola API dichiarativa. Immagini lazy, rivelazioni allo scroll e liste infinite si riducono tutte allo stesso schema: osservare, reagire a isIntersecting e smettere di osservare quando hai finito.