ResizeObserver ti avvisa quando le dimensioni di un elemento cambiano.
Prima di ResizeObserver, dovevi collegare un listener all'evento resize del documento
per ricevere una notifica di qualsiasi modifica delle dimensioni della finestra. Nel gestore
di eventi, dovrai quindi capire quali elementi sono stati interessati da
questa modifica e chiamare una routine specifica per reagire in modo appropriato. Se avevi bisogno
delle nuove dimensioni di un elemento dopo un ridimensionamento, dovevi chiamare
<x0A>getBoundingClientRect() o getComputedStyle(), il che può causare problemi di layout
<x0A>se non ti occupi di raggruppare tutte le letture e tutte le
<x0A>scritture.
Ciò non copriva nemmeno i casi in cui le dimensioni degli elementi cambiano senza che la finestra principale
sia stata ridimensionata. Ad esempio, l'aggiunta di nuovi elementi secondari, l'impostazione dello stile display di un elemento su none o azioni simili possono modificare le dimensioni di un elemento, dei relativi elementi di pari livello o dei relativi elementi principali.
Per questo motivo ResizeObserver è un elemento primitivo utile. Reagisce alle variazioni
di dimensione di uno qualsiasi degli elementi osservati, indipendentemente dalla causa della variazione.
Fornisce anche l'accesso alle nuove dimensioni degli elementi osservati.
API
Tutte le API con il suffisso Observer menzionato sopra condividono un design semplice. ResizeObserver non fa eccezione. Crea un oggetto ResizeObserver
e passa un callback al costruttore. Alla callback viene passato un array di oggetti ResizeObserverEntry, una voce per ogni elemento osservato, che contiene le nuove dimensioni dell'elemento.
var ro = new ResizeObserver(entries => {
for (let entry of entries) {
const cr = entry.contentRect;
console.log('Element:', entry.target);
console.log(`Element size: ${cr.width}px x ${cr.height}px`);
console.log(`Element padding: ${cr.top}px ; ${cr.left}px`);
}
});
// Observe one or multiple elements
ro.observe(someElement);
Alcuni dettagli
Che cosa viene segnalato?
In genere, un
ResizeObserverEntry
segnala la casella dei contenuti di un elemento tramite una proprietà chiamata
contentRect, che restituisce un oggetto
DOMRectReadOnly. La casella dei contenuti è la casella in cui possono essere inseriti i contenuti. È
il riquadro di delimitazione meno il padding.
È importante notare che, sebbene i report ResizeObserver mostrino sia le dimensioni del contentRect sia il padding, monitorano solo il contentRect.
Non confondere contentRect con il riquadro di delimitazione dell'elemento. Il riquadro
di selezione, come riportato da getBoundingClientRect(), è il riquadro che contiene
l'intero elemento e i relativi discendenti. Le immagini SVG fanno eccezione alla regola, in quanto
ResizeObserver riporta le dimensioni del riquadro di delimitazione.
A partire da Chrome 84, ResizeObserverEntry dispone di tre nuove proprietà per fornire informazioni più dettagliate. Ognuna di queste proprietà restituisce un oggetto ResizeObserverSize contenente una proprietà blockSize e una proprietà inlineSize. Queste
informazioni riguardano l'elemento osservato al momento della chiamata di callback.
borderBoxSizecontentBoxSizedevicePixelContentBoxSize
Tutti questi elementi restituiscono array di sola lettura perché in futuro si spera che possano supportare elementi con più frammenti, che si verificano in scenari a più colonne. Per ora, questi array conterranno un solo elemento.
Il supporto della piattaforma per queste proprietà è limitato, ma Firefox supporta già le prime due.
Quando viene segnalato?
La specifica prevede che ResizeObserver elabori tutti gli eventi di ridimensionamento
prima del rendering e dopo il layout. In questo modo, il callback di un ResizeObserver è
il luogo ideale per apportare modifiche al layout della pagina. Poiché l'elaborazione di ResizeObserver
avviene tra il layout e il rendering, questa operazione invaliderà solo
il layout, non il rendering.
Gotcha
Potresti chiederti: cosa succede se modifico le dimensioni di un elemento osservato all'interno del callback in ResizeObserver? La risposta è: verrà attivata
un'altra chiamata di callback immediatamente. Fortunatamente, ResizeObserver ha un meccanismo per evitare loop di callback infiniti e dipendenze cicliche. Le modifiche verranno
elaborate nello stesso frame solo se l'elemento ridimensionato si trova più in profondità nell'albero DOM
rispetto all'elemento meno profondo elaborato nel callback precedente.
In caso contrario, verranno rimandati al frame successivo.
Applicazione
Una delle cose che ResizeObserver ti consente di fare è implementare query multimediali per elemento. Osservando gli elementi, puoi definire in modo imperativo i punti di interruzione del design e modificare gli stili di un elemento. Nel seguente
esempio, il secondo riquadro
cambierà il raggio del bordo in base alla sua larghezza.
const ro = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.style.borderRadius =
Math.max(0, 250 - entry.contentRect.width) + 'px';
}
});
// Only observe the second box
ro.observe(document.querySelector('.box:nth-child(2)'));
Un altro esempio interessante da esaminare è una finestra di chat. Il problema che si presenta in un tipico layout di conversazione dall'alto verso il basso è il posizionamento dello scorrimento. Per evitare di confondere l'utente, è utile che la finestra rimanga nella parte inferiore della conversazione, dove vengono visualizzati i messaggi più recenti. Inoltre, qualsiasi tipo di cambio di layout (ad esempio, quando lo smartphone passa dall'orientamento orizzontale a quello verticale o viceversa) dovrebbe ottenere lo stesso risultato.
ResizeObserver ti consente di scrivere un unico codice che gestisce
entrambi gli scenari. Il ridimensionamento della finestra è un evento che un ResizeObserver può
acquisire per definizione, ma la chiamata di appendChild() ridimensiona anche l'elemento
(a meno che non sia impostato overflow: hidden), perché deve fare spazio per i nuovi
elementi. Tenendo presente questo aspetto, bastano poche righe per ottenere l'effetto desiderato:
const ro = new ResizeObserver(entries => {
document.scrollingElement.scrollTop =
document.scrollingElement.scrollHeight;
});
// Observe the scrollingElement for when the window gets resized
ro.observe(document.scrollingElement);
// Observe the timeline to process new messages
ro.observe(timeline);
Piuttosto comodo, no?
Da qui, potrei aggiungere altro codice per gestire il caso in cui l'utente abbia scorre manualmente verso l'alto e voglia che lo scorrimento si fermi su quel messaggio quando ne arriva uno nuovo.
Un altro caso d'uso riguarda qualsiasi tipo di elemento personalizzato che esegue il proprio layout.
Fino al giorno ResizeObserver, non esisteva un modo affidabile per ricevere una notifica quando le sue dimensioni cambiano, in modo che i suoi elementi secondari possano essere disposti di nuovo.
Effetti su Interaction to Next Paint (INP)
Interaction to Next Paint (INP) è una metrica che misura l'adattabilità complessiva di una pagina alle interazioni degli utenti. Se l'INP di una pagina rientra nella soglia "buona", ovvero 200 millisecondi o meno, si può dire che la pagina risponde in modo affidabile alle interazioni dell'utente.
Sebbene il tempo necessario per l'esecuzione dei callback degli eventi in risposta a un'interazione utente possa contribuire in modo significativo alla latenza totale di un'interazione, non è l'unico aspetto dell'INP da considerare. L'INP considera anche il tempo necessario per la successiva visualizzazione dell'interazione. ovvero il tempo necessario per completare il lavoro di rendering richiesto per aggiornare l'interfaccia utente in risposta a un'interazione.
Per quanto riguarda ResizeObserver, questo è importante perché il callback eseguito da un'istanza ResizerObserver si verifica poco prima del rendering. Questo
è intenzionale, in quanto il lavoro che si svolge nel callback deve essere preso in
considerazione, poiché il risultato di questo lavoro molto probabilmente richiederà una modifica all'interfaccia utente.
Fai attenzione a eseguire il minor lavoro di rendering possibile in un ResizeObserver
callback, in quanto un lavoro di rendering eccessivo può creare situazioni in cui il browser
ritarda l'esecuzione di attività importanti. Ad esempio, se un'interazione ha un
callback che causa l'esecuzione di un callback ResizeObserver, assicurati di fare
quanto segue per facilitare l'esperienza più fluida possibile:
- Assicurati che i selettori CSS siano il più semplici possibile per evitare un lavoro di ricalcolo dello stile eccessivo. I ricalcoli dello stile vengono eseguiti appena prima del layout e i selettori CSS complessi possono ritardare le operazioni di layout.
- Evita di eseguire qualsiasi operazione nel callback
ResizeObserverche possa attivare reflow forzati. - Il tempo necessario per aggiornare il layout di una pagina aumenta generalmente con il
numero di elementi DOM in una pagina. Anche se questo vale indipendentemente dal fatto che le pagine utilizzino
ResizeObserver, il lavoro svolto in un callbackResizeObserverpuò diventare significativo man mano che la complessità strutturale di una pagina aumenta.
Conclusione
ResizeObserver è disponibile in tutti i principali
browser
e fornisce un modo efficiente per monitorare i ridimensionamenti degli elementi a livello di elemento. Fai solo attenzione a non ritardare troppo il rendering con questa potente API.