ResizeObserver
ti consente di sapere quando le dimensioni di un elemento cambiano.
Prima del giorno ResizeObserver
, dovevi collegare un listener all'evento resize
del documento per ricevere una notifica per qualsiasi modifica delle dimensioni dell'area visibile. Nel gestore di eventi, devi capire quali elementi sono stati interessati dalla modifica e chiamare una routine specifica affinché reagisca in modo appropriato. Se avevi bisogno
delle nuove dimensioni di un elemento dopo un ridimensionamento, dovevi chiamare
getBoundingClientRect()
o getComputedStyle()
, il che può causare una
thrash del layout se non ti occupi di raggruppare tutte le letture e tutte le scritture.
Non sono stati inclusi 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 predecessori.
Questo è il motivo per cui ResizeObserver
è una funzione primitiva utile. Reagisce ai cambiamenti nelle dimensioni di qualsiasi elemento osservato, indipendentemente da ciò che ha causato il cambiamento.
Fornisce anche l'accesso alla nuova dimensione degli elementi osservati.
API
Tutte le API con il suffisso Observer
sopra menzionato hanno un design semplice. ResizeObserver
non fa eccezione. Crei un oggetto ResizeObserver
e passi un callback al costruttore. Al callback viene passato un array di
ResizeObserverEntry
oggetti, una voce per elemento osservato,
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 elemento
ResizeObserverEntry
segnala la casella dei contenuti di un elemento tramite una proprietà denominata
contentRect
, che restituisce un oggetto
DOMRectReadOnly
. La casella dei contenuti è la casella in cui è possibile inserire i contenuti. È la casella del bordo senza la spaziatura interna.
È importante notare che, sebbene ResizeObserver
riporti sia le dimensioni
di contentRect
che la spaziatura interna, guarda solo contentRect
.
Non confondere contentRect
con il riquadro di delimitazione dell'elemento. Il riquadro di delimitazione, come riportato da getBoundingClientRect()
, è il riquadro che contiene l'intero elemento e i relativi discendenti. Le immagini SVG sono un'eccezione alla regola in cui ResizeObserver
segnalerà le dimensioni del riquadro di delimitazione.
A partire da Chrome 84, ResizeObserverEntry
ha 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 del callback.
borderBoxSize
contentBoxSize
devicePixelContentBoxSize
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 con più colonne. Per ora, questi array conterranno un solo elemento.
Il supporto della piattaforma per queste proprietà è limitato, ma Firefox già supporta le prime due.
Quando vengono segnalati?
La specifica indica che ResizeObserver
deve elaborare tutti gli eventi di ridimensionamento
prima della visualizzazione e dopo il layout. Questo rende il callback di ResizeObserver
il
luogo ideale per apportare modifiche al layout della pagina. Poiché l'elaborazione ResizeObserver
avviene tra layout e Paint, questa operazione annullerà solo il layout, non il disegno.
Trovati
Forse ti starai chiedendo: cosa succede se modifico le dimensioni di un elemento
osservato all'interno del callback in ResizeObserver
? La risposta è: attiverai subito
un'altra chiamata per il callback. Fortunatamente, ResizeObserver
ha un meccanismo per evitare infiniti loop di callback e dipendenze cicliche. Le modifiche verranno elaborate nello stesso frame solo se l'elemento ridimensionato si trova più in basso nell'albero DOM rispetto all'elemento shallowest elaborato nel callback precedente.
In caso contrario, verranno rimandati al frame successivo.
Applicazione
ResizeObserver
consente di 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, la seconda casella modificherà il raggio del bordo in base alla 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 è la finestra della chat. Il problema che si pone in un layout tipico di una conversazione dall'alto verso il basso è il posizionamento tramite scorrimento. Per evitare di confondere l'utente, è utile che la finestra sia fissata in fondo alla conversazione, dove sono visualizzati i messaggi più recenti. Inoltre, qualsiasi tipo di modifica del layout (ad esempio, se passi da un telefono da orizzontale a verticale o viceversa) dovrebbe ottenere lo stesso risultato.
ResizeObserver
consente di scrivere una singola porzione di codice che si occupa di
entrambi gli scenari. Il ridimensionamento della finestra è un evento che ResizeObserver
può
acquisire per definizione, ma la chiamata a appendChild()
ridimensiona anche quell'elemento
(a meno che non sia impostato overflow: hidden
), perché deve fare spazio per i nuovi
elementi. Tenendo conto di questo, sono necessarie pochissime 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 ha fatto scorrere la pagina verso l'alto manualmente e vuole rimanere in attesa di quel messaggio quando arriva un nuovo messaggio.
Un altro caso d'uso è per qualsiasi tipo di elemento personalizzato con un layout proprio.
Fino al giorno ResizeObserver
non esisteva un modo affidabile per ricevere notifiche quando le dimensioni
cambiavano, quindi i relativi elementi secondari possono essere configurati di nuovo.
Effetti sull'interazione con il colore successivo (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 "valida", ovvero massimo 200 millisecondi, si può affermare che una pagina risponde in modo affidabile alle interazioni dell'utente con la pagina.
Sebbene il tempo necessario per l'esecuzione dei callback di eventi in risposta a un'interazione dell'utente possa contribuire in modo significativo alla latenza totale di un'interazione, questo non è l'unico aspetto di INP da considerare. INP tiene conto anche del tempo necessario per eseguire la visualizzazione successiva dell'interazione. Si tratta del tempo necessario per il lavoro di rendering necessario per aggiornare l'interfaccia utente in risposta al completamento di un'interazione.
Per quanto riguarda ResizeObserver
, questo aspetto è importante perché il callback che esegue un'istanza ResizerObserver
avviene immediatamente prima del funzionamento del rendering. Questo è stato progettato, poiché il lavoro svolto nel callback deve essere preso in considerazione e il risultato di tale operazione molto probabilmente richiederà una modifica all'interfaccia utente.
Cerca di non occupare troppo tempo di rendering in un callback ResizeObserver
, poiché un eccessivo lavoro di rendering può creare situazioni in cui il browser subisce ritardi nelle operazioni importanti. Ad esempio, se un'interazione prevede un callback che causa l'esecuzione di un callback ResizeObserver
, assicurati di svolgere le seguenti operazioni per semplificare il più possibile l'esperienza:
- Assicurati che i selettori CSS siano il più semplici possibile per evitare un lavoro eccessivo di ricalcolo dello stile. I ricalcoli degli stili vengono eseguiti appena prima del layout e i selettori CSS complessi possono ritardare le operazioni di layout.
- Evita di eseguire operazioni nel callback
ResizeObserver
che possano attivare ripetizioni forzate. - Il tempo necessario per aggiornare il layout di una pagina in genere aumenta con il numero di elementi DOM sulla pagina. Anche se questo è vero indipendentemente dal fatto che le pagine utilizzino o meno
ResizeObserver
, il lavoro svolto in un callbackResizeObserver
può diventare significativo con l'aumento della complessità strutturale di una pagina.
Conclusione
ResizeObserver
è disponibile in tutti i principali browser e consente di monitorare in modo efficiente il ridimensionamento degli elementi a livello di elemento. Fai solo attenzione a non ritardare troppo il rendering con questa potente API.