// apis
Che cos'è ResizeObserver? Il ridimensionamento a livello di elemento, e l'errore di loop che capita a tutti
L'evento resize di window non dice nulla su un singolo elemento. ResizeObserver sì. Che cosa riporta, la differenza tra content box e border box, e perché in console compare «ResizeObserver loop completed with undelivered notifications».
L'evento resize su window scatta quando cambia il viewport. È tutto ciò che sa. Un elemento può cambiare dimensione per un contenitore flex, per una barra laterale che si apre, per un font che finisce di caricarsi o per un textarea trascinato, e window non ne saprà mai nulla.
ResizeObserver è l'API che sorveglia un elemento anziché la finestra.
Che cosa riporta davvero
MDN lo formula con precisione: l'interfaccia «riporta le variazioni delle dimensioni della content box o della border box di un Element, o della bounding box di un SVGElement».
Quelle due box non sono la stessa cosa, e scegliere quella sbagliata è una classica fonte di errori di pochi pixel. La content box è dove va il contenuto: la border box meno padding e bordo. La border box racchiude insieme contenuto, padding e bordo.
const observer = new ResizeObserver((entries) => {
for (const entry of entries) {
const { width, height } = entry.contentRect;
entry.target.classList.toggle('is-narrow', width < 400);
}
});
observer.observe(document.querySelector('.card')); Il callback riceve un array di voci perché un solo observer può sorvegliare più elementi e diversi possono cambiare nello stesso frame. Leggere entry.contentRect dà la content box; entry.borderBoxSize e entry.contentBoxSize danno le box come array di oggetti di dimensioni.
L'errore che capita a tutti: «loop completed with undelivered notifications»
Prima o poi compare in console, e merita di essere capito anziché messo a tacere.
Il problema da cui protegge è circolare: il tuo callback cambia la dimensione di qualcosa, quel cambiamento riattiva l'observer, che cambia di nuovo la dimensione. Lasciata a sé, la pagina non si stabilizzerebbe mai.
// Boucle: le callback change la taille de ce qu il observe
const bad = new ResizeObserver(([entry]) => {
entry.target.style.height = entry.contentRect.height + 1 + 'px';
}); La risposta della specifica, nelle parole di MDN, è che «i loop infiniti dovuti a dipendenze cicliche sono affrontati elaborando a ogni iterazione solo gli elementi più profondi nel DOM». Tutto ciò che non soddisfa quella condizione è rinviato al paint successivo, e su window viene emesso un evento di errore con un messaggio fisso:
ResizeObserver loop completed with undelivered notifications.
Ecco la parte che conta di più, e MDN la enuncia senza giri di parole: «questo impedisce soltanto il blocco del browser, non il loop infinito in sé». Il browser protegge sé stesso, non sistema il tuo codice. Il messaggio è un sintomo, e il loop è ancora lì.
Potresti vedere anche ResizeObserver loop limit exceeded, una vecchia formulazione di Chrome per la stessa famiglia di problema. Stringa diversa, stessa cosa da correggere.
Come smettere di provocarlo
Non scrivere nel callback nulla che cambi la dimensione di ciò che osservi. Alternare una classe che cambia solo un colore è innocuo. Impostare un'altezza a partire da un'altezza misurata è il loop da manuale.
Osserva il genitore, stila il figlio. Poiché l'algoritmo elabora prima gli elementi più profondi nel DOM, agire verso il basso anziché verso l'alto ti tiene dalla parte giusta.
Rinvia la scrittura. Avvolgere la mutazione in requestAnimationFrame la toglie dal passaggio di osservazione. Consideralo un ripiego: se la dipendenza è davvero circolare, l'hai rimandata, non eliminata.
Quando non serve affatto
Se vuoi soltanto stilare un componente in base alla sua larghezza, le container query CSS lo fanno in CSS, senza callback né loop di cui preoccuparsi. Tieni ResizeObserver per quando ti serve il numero in JavaScript: ridisegnare un canvas, impaginare un grafico, ricalcolare qualcosa che il CSS non sa esprimere.
E se stai aspettando che un elemento entri nel viewport anziché cambiare dimensione, quello è tutt'altro observer: vedi la Intersection Observer API.
In breve
ResizeObserver è la risposta a livello di elemento a un evento a livello di finestra. Scegli la box giusta, non ridimensionare mai dal callback ciò che osservi, e leggi l'errore di loop come un segnale vero e non come rumore di console: il browser si è soltanto impedito di bloccarsi, il ciclo non l'ha risolto.