IntersectionObserver ti consente di sapere quando un elemento osservato entra o esce dall'area visibile del browser.
Supponiamo di voler monitorare quando un elemento del DOM entra nell'area visibile visibile. Potresti volerlo fare per caricare le immagini in modo lazy just in time o perché devi sapere se l'utente sta effettivamente guardando un determinato banner dell'annuncio. Puoi farlo collegando l'evento di scorrimento o utilizzando un timer periodico e chiamando getBoundingClientRect()
su quell'elemento.
Tuttavia, questo approccio è estremamente lento perché ogni chiamata a getBoundingClientRect()
costringe il browser a riorganizzare l'intera pagina e introduce notevoli problemi di aggiornamento nel tuo sito web. Le cose si complicano quando sai che il tuo sito viene caricato all'interno di un iframe e vuoi sapere quando l'utente può vedere un elemento. Il modello a origine singola e il browser non ti permettono di accedere ai dati della pagina web che contiene l'iframe. Questo è un problema comune, ad esempio, per gli annunci che vengono caricati di frequente utilizzando iframe.
IntersectionObserver
è stato progettato per rendere più efficiente questo test di visibilità ed è disponibile in tutti i browser moderni. IntersectionObserver
ti consente di sapere quando un elemento osservato entra o esce dall'area visibile del browser.
Come creare un IntersectionObserver
L'API è piuttosto piccola e può essere descritta meglio utilizzando un esempio:
const io = new IntersectionObserver(entries => {
console.log(entries);
}, {
/* Using default options. Details below */
});
// Start observing an element
io.observe(element);
// Stop observing an element
// io.unobserve(element);
// Disable entire IntersectionObserver
// io.disconnect();
Se utilizzi le opzioni predefinite per IntersectionObserver
, il callback verrà chiamato sia quando l'elemento viene visualizzato parzialmente sia quando esce completamente dall'area visibile.
Se devi osservare più elementi, è possibile e consigliabile farlo utilizzando la stessa istanza IntersectionObserver
chiamando observe()
più volte.
Al callback viene passato un parametro entries
, che è un array di oggetti IntersectionObserverEntry
. Ciascuno di questi oggetti contiene dati aggiornati sull'intersezione per uno degli elementi osservati.
🔽[IntersectionObserverEntry]
time: 3893.92
🔽rootBounds: ClientRect
bottom: 920
height: 1024
left: 0
right: 1024
top: 0
width: 920
🔽boundingClientRect: ClientRect
// ...
🔽intersectionRect: ClientRect
// ...
intersectionRatio: 0.54
🔽target: div#observee
// ...
rootBounds
è il risultato della chiamata getBoundingClientRect()
sull'elemento principale, che per impostazione predefinita è l'area visibile. boundingClientRect
è il risultato di getBoundingClientRect()
chiamato sull'elemento osservato. intersectionRect
è l'intersezione di questi due rettangoli e indica in modo efficace quale parte dell'elemento osservato è visibile. intersectionRatio
è strettamente correlato e indica quanto dell'elemento è visibile. Con queste informazioni a tua disposizione, ora puoi implementare funzionalità come il caricamento just-in-time degli asset prima che diventino visibili sullo schermo. In modo efficiente.
IntersectionObserver
invia i dati in modo asincrono e il codice di callback verrà eseguito nel thread principale. Inoltre, le specifiche indicano in realtà che le implementazioni IntersectionObserver
devono utilizzare requestIdleCallback()
. Ciò significa che la chiamata al callback fornito ha una priorità bassa e verrà effettuata dal browser durante il tempo di inattività. Si tratta di una decisione di design consapevole.
Elementi div con scorrimento
Lo scorrimento all'interno di un elemento non mi piace molto, ma non sono qui per giudicare e nemmeno IntersectionObserver
. L'oggetto options
accetta un'opzione root
che ti consente di definire un'alternativa al viewport come elemento principale. È importante tenere presente che root
deve essere un antenato di tutti gli elementi osservati.
Intersezione di tutte le cose.
No! Cattivo sviluppatore! Questo non rappresenta l'utilizzo consapevole dei cicli della CPU dell'utente. Prendiamo ad esempio uno scroller infinito: in questo caso, è consigliabile aggiungere sentinelle al DOM e osservarle (e riciclarle!). Devi aggiungere un elemento sentinella vicino all'ultimo elemento nello scorrimento infinito. Quando viene visualizzata la sentinella, puoi utilizzare il callback per caricare i dati, creare gli elementi successivi, collegarli al DOM e riposizionare la sentinella di conseguenza. Se ricicli correttamente il sentinel, non è necessaria un'ulteriore chiamata a observe()
. IntersectionObserver
continua a funzionare.
Altri aggiornamenti, per favore
Come accennato in precedenza, il callback verrà attivato una sola volta quando l'elemento osservato diventa parzialmente visibile e un'altra volta quando ha lasciato l'area visibile. In questo modo, IntersectionObserver
ti fornisce una risposta alla domanda "L'elemento X è visibile?". Tuttavia, in alcuni casi d'uso, potrebbe non essere sufficiente.
È qui che entra in gioco l'opzione threshold
. Consente di definire un array di soglie intersectionRatio
. Il callback verrà chiamato ogni volta che intersectionRatio
supera uno di questi valori. Il valore predefinito di threshold
è [0]
e spiega il comportamento predefinito. Se cambiamo threshold
in [0, 0.25, 0.5, 0.75, 1]
, riceveremo una notifica ogni volta che un altro quarto dell'elemento diventa visibile:
Altre opzioni?
Al momento, esiste un'unica opzione aggiuntiva rispetto a quelle elencate sopra. rootMargin
consente di specificare i margini per la radice, in modo da aumentare o ridurre l'area utilizzata per le incroci. Questi margini vengono specificati mediante una stringa in stile CSS, ovvero la "10px 20px 30px 40px"
, che specifica rispettivamente i margini superiore, destro, inferiore e sinistro. In sintesi, la struttura di opzioni IntersectionObserver
offre le seguenti opzioni:
new IntersectionObserver(entries => {/* … */}, {
// The root to use for intersection.
// If not provided, use the top-level document's viewport.
root: null,
// Same as margin, can be 1, 2, 3 or 4 components, possibly negative lengths.
// If an explicit root element is specified, components may be percentages of the
// root element size. If no explicit root element is specified, using a
// percentage is an error.
rootMargin: "0px",
// Threshold(s) at which to trigger callback, specified as a ratio, or list of
// ratios, of (visible area / total area) of the observed element (hence all
// entries must be in the range [0, 1]). Callback will be invoked when the
// visible ratio of the observed element crosses a threshold in the list.
threshold: [0],
});
<iframe>
magico
I IntersectionObserver
sono stati progettati appositamente tenendo conto dei servizi pubblicitari e dei widget dei social network, che utilizzano spesso elementi <iframe>
e potrebbero trarre vantaggio dal sapere se sono visibili. Se un <iframe>
osserva uno dei suoi elementi, lo scorrimento dell'elemento <iframe>
e della finestra che contiene l'elemento <iframe>
attiveranno il callback nei momenti opportuni. Tuttavia, in quest'ultimo caso, rootBounds
verrà impostato su null
per evitare la fuga di dati tra le origini.
Di cosa parla IntersectionObserver
Non?
Tieni presente che IntersectionObserver
non è intenzionalmente né con una qualità pixel perfetta né a bassa latenza. Se li utilizzi per implementare iniziative come animazioni dipendenti dallo scorrimento, il risultato sarà fallimentare, in quanto i dati saranno, in senso stretto, obsoleti quando li utilizzerai. L'articolo esplicativo contiene ulteriori dettagli sui casi d'uso originali di IntersectionObserver
.
Quanto lavoro posso fare nel callout?
Breve e conciso: se il callback richiede troppo tempo, l'app subirà un ritardo. Si applicano tutte le pratiche comuni.
Vai avanti e interseca i tuoi elementi
Il supporto del browser per IntersectionObserver
è buono, poiché è disponibile in tutti i browser moderni. Se necessario, è possibile utilizzare un polyfill nei browser meno recenti ed è disponibile nel repository del WICD. Ovviamente, utilizzando il polyfill non otterrai i vantaggi in termini di prestazioni di un'implementazione nativa.
Puoi iniziare a utilizzare IntersectionObserver
subito. Dicci cosa hai trovato.