Introduzione
Le performance sono il nuovo SEO. Google, Core Web Vitals e la crescente attenzione degli utenti ai siti veloci rendono fondamentale alleggerire il JavaScript e rimandare il caricamento di ciò che non serve subito. L’Intersection Observer API (IO API) è arrivata come risposta elegante a due bisogni chiave del frontend moderno:
- Animazioni on‑scroll che non dipendano da costosi handler di
scroll
chiamati a ogni pixel. - Lazy loading di immagini, video o componenti heavy senza jank né layout shift.
Se finora hai usato librerie come AOS, Waypoints o plugin jQuery, preparati: con poche righe vanilla JS puoi sostituire tutto, migliorare la Largest Contentful Paint (approfondita qui → Ottimizzare LCP per siti più veloci) e ridurre il bundle. In questa guida pratica—pensata per dev italiani di livello medio‑avanzato—scoprirai il perché tecnico e il come operativo dell’Intersection Observer.
1 · Come funziona tecnicamente Intersection Observer
1.1 Il problema con scroll
Ascoltare l’evento scroll
significa eseguire una callback decine di volte al secondo. Anche con debounce/throttle interferisci con il main thread e rischi jank. L’IO API delega tutto al browser.
1.2 Glossario minimo
Term | Descrizione |
---|---|
Target | L’elemento da osservare. |
Root | Il viewport o un container scrollabile. Default: null (= document ). |
Threshold | Valori 0‑1 che indicano quanta parte del target deve essere visibile prima di triggerare la callback. |
rootMargin | Margine virtuale attorno al root; si imposta come nel CSS ("0px 0px -25% 0px" ). |
1.3 Alta frequenza, basso costo
L’osservatore gira su un thread separato e notifica la callback solo ai cambi di stato di intersezione; il browser può accorpare notifiche e ottimizzare.
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// ...azione...
}
});
}, {
threshold: 0.25,
rootMargin: "0px 0px -10% 0px" // pre‑trigger 10% prima di entrare
});
Questo pattern rende elegante anche la disconnessione (observer.unobserve(el)
), cruciale per non leakare memory.
Tip: nelle SPA React/Vue rispetta il ciclo di vita (es. useEffect
cleanup).
Un approfondimento SEO correlato: Checklist SEO tecnica per sviluppatori frontend.
2 · Esempio pratico 1 — Animare un elemento al viewport
Obiettivo: far comparire un blocco con fade‑in + translate Y solo la prima volta che entra nel viewport.
2.1 Markup minimal
<section class="features">
<article class="feature-card hidden">
<h3>Performance al massimo</h3>
<p>Riduci il _main thread_ con IO API.</p>
</article>
<!-- Altre card -->
</section>
2.2 CSS (solo 3 classi!)
.feature-card{
opacity:1;transform:translateY(0);transition:all .6s ease-out;
}
.hidden{opacity:0;transform:translateY(40px);} /* stato iniziale */
2.3 JavaScript
const cards = document.querySelectorAll('.feature-card');
const revealOnce = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if(entry.isIntersecting){
entry.target.classList.remove('hidden');
obs.unobserve(entry.target); // reveal one‑shot
}
});
},{threshold:0.2});
cards.forEach(card => revealOnce.observe(card));
Perché preferirlo ad AOS? Zero dipendenze, nessun polling, pieno controllo su threshold e rootMargin.
Approfondisci librerie CSS-only: Animazioni CSS moderne: guida pratica.
3 · Esempio pratico 2 — Lazy loading delle immagini
Il tag loading="lazy"
copre l’80 % dei casi, ma non sempre: slider custom, Safari < 16, gestione di placeholder. Intersection Observer colma il gap.
3.1 Markup con placeholder
<img class="lazy" data-src="/img/hero.jpg" alt="Esempio Lazy" width="600" height="400" />
3.2 Script lazy‑loader
const lazyImgs = document.querySelectorAll('img.lazy');
const loadImg = (entry) => {
const img = entry.target;
img.src = img.dataset.src;
img.onload = () => img.classList.add('loaded');
};
const ioLazy = new IntersectionObserver((entries, obs)=>{
entries.forEach(entry=>{
if(entry.isIntersecting){
loadImg(entry);
obs.unobserve(entry.target);
}
});
},{rootMargin:'0px 0px 200px 0px'}); // pre‑carica 200 px prima
lazyImgs.forEach(img=> ioLazy.observe(img));
Abbiamo:
- rootMargin positivo ⇒ pre‑fetch per evitare jank.
- Disconnessione dopo il caricamento.
width/height
espliciti per eliminare il cumulative layout shift (CLS).
Consulta anche la guida su Ottimizzare le immagini con picture e srcset per un approccio combinato.
4 · Best practice ed errori da evitare
- Sovrappopolare l’Observer » mantieni gruppi omogenei (one observer per tipo).
- Threshold = 0 » buono per lazy load, pessimo per animazioni: rischi flicker.
- rootMargin errato » Evita valori negativi che tagliano l’area utile.
- Dimenticare la disconnessione »
unobserve
odisconnect
incomponentWillUnmount
/useEffect
cleanup. - Accessibilità » le animazioni devono rispettare
prefers-reduced-motion
.@media (prefers-reduced-motion: reduce){ .feature-card{transition:none;transform:none;} }
- SEO » Markup semantico
section/article
+loading="lazy"
+alt
testi accurati (vedi HTML Semantico).
Per checklist completa: 5 errori comuni nei progetti frontend e come evitarli.
Conclusione
L’Intersection Observer è la strategia standard per:
- Eliminare handler di scroll costosi.
- Creare animazioni eleganti e performant‑friendly.
- Caricare risorse solo quando servono, migliorando LCP e CLS.
Vuoi spingere oltre le tue animazioni? Leggi → Animazioni CSS moderne: guida pratica e trasforma il tuo UI in esperienza WOW!