// apis
Was ist ResizeObserver? Größenänderung auf Elementebene, und der Schleifenfehler, den jeder trifft
Das resize-Ereignis von window sagt nichts über ein einzelnes Element. ResizeObserver schon. Was er meldet, der Unterschied zwischen Content-Box und Border-Box, und warum „ResizeObserver loop completed with undelivered notifications“ in Ihrer Konsole auftaucht.
Das resize-Ereignis auf window feuert, wenn sich der Viewport ändert. Mehr weiß es nicht. Ein Element kann durch einen Flex-Container, eine sich öffnende Seitenleiste, eine endlich geladene Schrift oder ein gezogenes Textarea seine Größe ändern - window erfährt davon nie etwas.
ResizeObserver ist die API, die ein Element statt des Fensters beobachtet.
Was er tatsächlich meldet
MDN formuliert es präzise: die Schnittstelle „meldet Änderungen der Abmessungen der Content- oder Border-Box eines Element oder der Bounding-Box eines SVGElement“.
Diese beiden Boxen sind nicht dasselbe, und die falsche zu wählen ist eine klassische Quelle für Fehler von wenigen Pixeln. Die Content-Box ist der Ort für den Inhalt: die Border-Box abzüglich Padding und Rahmen. Die Border-Box umschließt Inhalt, Padding und Rahmen gemeinsam.
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')); Der Callback erhält ein Array von Einträgen, weil ein Observer viele Elemente beobachten kann und sich mehrere im selben Frame ändern können. entry.contentRect liefert die Content-Box; entry.borderBoxSize und entry.contentBoxSize liefern die Boxen als Arrays von Dimensionsobjekten.
Der Fehler, den jeder trifft: „loop completed with undelivered notifications“
Früher oder später steht er in der Konsole, und er verdient Verständnis statt Stummschaltung.
Das Problem, vor dem er schützt, ist zirkulär: Ihr Callback ändert die Größe von etwas, diese Änderung löst den Observer erneut aus, der wieder die Größe ändert. Sich selbst überlassen, käme die Seite nie zur Ruhe.
// 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';
}); Die Antwort der Spezifikation lautet, in MDNs Worten, dass „Endlosschleifen aus zyklischen Abhängigkeiten dadurch angegangen werden, dass in jeder Iteration nur tiefer im DOM liegende Elemente verarbeitet werden“. Alles, was diese Bedingung nicht erfüllt, wird auf den nächsten Paint verschoben, und auf window wird ein Fehlerereignis mit einer festen Meldung ausgelöst:
ResizeObserver loop completed with undelivered notifications.
Hier ist der wichtigste Teil, und MDN sagt ihn unumwunden: „das verhindert nur ein Einfrieren des Browsers, nicht die Endlosschleife selbst“. Der Browser schützt sich selbst, er repariert Ihren Code nicht. Die Meldung ist ein Symptom, und die Schleife besteht weiter.
Möglicherweise sehen Sie auch ResizeObserver loop limit exceeded, eine ältere Chrome-Formulierung für dieselbe Problemfamilie. Andere Zeichenkette, dasselbe zu beheben.
Wie Sie aufhören, ihn auszulösen
Schreiben Sie im Callback nichts, was die Größe des Beobachteten ändert. Eine Klasse umzuschalten, die nur eine Farbe ändert, ist unbedenklich. Eine Höhe aus einer gemessenen Höhe zu setzen, ist die Lehrbuchschleife.
Beobachten Sie das Elternelement, stylen Sie das Kind. Da der Algorithmus tiefer liegende Elemente zuerst verarbeitet, hält Sie ein Wirken nach unten statt nach oben auf der richtigen Seite.
Verschieben Sie den Schreibvorgang. Die Mutation in requestAnimationFrame zu verpacken, nimmt sie aus dem Beobachtungsdurchlauf heraus. Betrachten Sie das als Notlösung: Ist die Abhängigkeit wirklich zirkulär, haben Sie sie aufgeschoben, nicht beseitigt.
Wann Sie ihn gar nicht brauchen
Wollen Sie nur eine Komponente nach ihrer eigenen Breite stylen, erledigen das CSS Container Queries in CSS, ohne Callback und ohne Schleifensorgen. Greifen Sie zu ResizeObserver, wenn Sie die Zahl selbst in JavaScript brauchen: ein Canvas neu zeichnen, ein Diagramm layouten, etwas neu berechnen, das CSS nicht ausdrücken kann.
Und wenn Sie darauf warten, dass ein Element in den Viewport tritt, statt seine Größe zu ändern, ist das ein ganz anderer Observer: siehe die Intersection Observer API.
Kurz gefasst
ResizeObserver ist die Antwort auf Elementebene für ein Ereignis auf Fensterebene. Wählen Sie die richtige Box, ändern Sie im Callback nie die Größe des Beobachteten, und lesen Sie den Schleifenfehler als echtes Signal statt als Konsolenrauschen: Der Browser hat sich nur vor dem Einfrieren bewahrt, den Zyklus hat er nicht behoben.