RidimensionaObservationr: è come document.onresize per gli elementi

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.

Supporto dei browser

  • 64
  • 79
  • 69
  • 13.1

Fonte

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.

Diagramma del modello di box del CSS.

È 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 callback ResizeObserver 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.