Mit ResizeObserver
können Sie sich benachrichtigen lassen, wenn sich die Größe eines Elements ändert.
Vor ResizeObserver
mussten Sie einen Listener an das resize
-Ereignis des Dokuments anhängen, um über Änderungen der Abmessungen des Viewports benachrichtigt zu werden. In der Ereignis-Handler-Funktion müssten Sie dann herausfinden, welche Elemente von dieser Änderung betroffen sind, und eine bestimmte Routine aufrufen, um angemessen zu reagieren. Wenn Sie die neuen Abmessungen eines Elements nach einer Größenänderung benötigten, mussten Sie getBoundingClientRect()
oder getComputedStyle()
aufrufen. Dies kann zu Layout-Thrashing führen, wenn Sie nicht darauf achten, alle Lese- und alle Schreibvorgänge zu bündeln.
Das betraf nicht einmal Fälle, in denen sich die Größe von Elementen ändert, ohne dass das Hauptfenster angepasst wurde. Wenn Sie beispielsweise neue untergeordnete Elemente anhängen, den display
-Stil eines Elements auf none
festlegen oder ähnliche Aktionen ausführen, kann sich die Größe eines Elements, seiner Geschwister oder seiner übergeordneten Elemente ändern.
Deshalb ist ResizeObserver
eine nützliche Primitive. Sie reagiert auf Änderungen der Größe eines der beobachteten Elemente, unabhängig davon, was die Änderung verursacht hat.
Außerdem haben Sie Zugriff auf die neue Größe der beobachteten Elemente.
API
Alle oben erwähnten APIs mit dem Suffix Observer
haben ein einfaches API-Design. ResizeObserver
ist da keine Ausnahme. Sie erstellen ein ResizeObserver
-Objekt und übergeben einen Callback an den Konstruktor. An den Callback wird ein Array von ResizeObserverEntry
-Objekten übergeben – ein Eintrag pro beobachtetem Element –, das die neuen Abmessungen für das Element enthält.
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);
Einige Details
Was wird gemeldet?
Im Allgemeinen wird das Inhaltsfeld eines Elements über eine Eigenschaft namens contentRect
gemeldet, die ein DOMRectReadOnly
-Objekt zurückgibt.ResizeObserverEntry
Das Inhaltsfeld ist das Feld, in dem Inhalte platziert werden können. Er entspricht dem Begrenzungsrahmen abzüglich des Innenabstands.

ResizeObserver
Meldet zwar sowohl die Abmessungen des contentRect
als auch das Padding, beobachtet aber nur das contentRect
.
Verwechseln Sie contentRect
nicht mit dem Begrenzungsrahmen des Elements. Der von getBoundingClientRect()
gemeldete Begrenzungsrahmen ist der Rahmen, der das gesamte Element und seine untergeordneten Elemente enthält. SVGs sind eine Ausnahme von der Regel. Hier gibt ResizeObserver
die Abmessungen des Begrenzungsrahmens an.
Ab Chrome 84 hat ResizeObserverEntry
drei neue Eigenschaften, um detailliertere Informationen zu liefern. Jede dieser Eigenschaften gibt ein ResizeObserverSize
-Objekt mit den Eigenschaften blockSize
und inlineSize
zurück. Diese Informationen beziehen sich auf das beobachtete Element zum Zeitpunkt des Rückrufs.
borderBoxSize
contentBoxSize
devicePixelContentBoxSize
Alle diese Elemente geben schreibgeschützte Arrays zurück, da sie in Zukunft hoffentlich Elemente mit mehreren Fragmenten unterstützen können, die in mehrspaltigen Szenarien auftreten. Derzeit enthalten diese Arrays nur ein Element.
Die Plattformunterstützung für diese Eigenschaften ist begrenzt, aber Firefox unterstützt bereits die ersten beiden.
Wann wird es gemeldet?
Die Spezifikation schreibt vor, dass ResizeObserver
alle Größenänderungsereignisse vor dem Rendern und nach dem Layout verarbeiten soll. Daher ist der Callback eines ResizeObserver
der ideale Ort, um Änderungen am Layout Ihrer Seite vorzunehmen. Da die Verarbeitung von ResizeObserver
zwischen Layout und Rendern erfolgt, wird dadurch nur das Layout ungültig gemacht, nicht das Rendern.
Gotcha
Sie fragen sich vielleicht, was passiert, wenn Sie die Größe eines beobachteten Elements im Callback zu ResizeObserver
ändern. Die Antwort lautet: Sie lösen sofort einen weiteren Aufruf des Callbacks aus. Glücklicherweise verfügt ResizeObserver
über einen Mechanismus, um Endlosschleifen und zyklische Abhängigkeiten zu vermeiden. Änderungen werden nur im selben Frame verarbeitet, wenn sich das Element mit der neuen Größe tiefer im DOM-Baum befindet als das oberflächlichste Element, das im vorherigen Callback verarbeitet wurde.
Andernfalls werden sie auf den nächsten Frame verschoben.
Anwendung
Mit ResizeObserver
können Sie unter anderem Media-Queries für einzelne Elemente implementieren. Durch das Beobachten von Elementen können Sie Ihre Design-Breakpoints imperativ definieren und die Stile eines Elements ändern. Im folgenden Beispiel ändert sich der Grenzradius des zweiten Felds entsprechend seiner Breite.
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)'));
Ein weiteres interessantes Beispiel ist ein Chatfenster. Das Problem, das bei einem typischen Layout von oben nach unten auftritt, ist die Scrollpositionierung. Damit der Nutzer nicht verwirrt wird, sollte das Fenster am unteren Rand des Unterhaltungsfensters bleiben, wo die neuesten Nachrichten angezeigt werden. Außerdem sollte jede Art von Layoutänderung (z. B. wenn ein Smartphone vom Quer- ins Hochformat wechselt oder umgekehrt) dasselbe Ergebnis erzielen.
Mit ResizeObserver
können Sie einen Code schreiben, der beide Szenarien abdeckt. Das Ändern der Größe des Fensters ist ein Ereignis, das ein ResizeObserver
per Definition erfassen kann. Durch den Aufruf von appendChild()
wird jedoch auch die Größe dieses Elements geändert (sofern overflow: hidden
nicht festgelegt ist), da Platz für die neuen Elemente geschaffen werden muss. Dazu sind nur wenige Zeilen erforderlich:
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);
Ziemlich praktisch, oder?
Von hier aus könnte ich weiteren Code hinzufügen, um den Fall zu behandeln, in dem der Nutzer manuell nach oben gescrollt hat und das Scrollen bei einer neuen Nachricht an dieser Nachricht bleiben soll.
Ein weiterer Anwendungsfall sind benutzerdefinierte Elemente, die ihr eigenes Layout erstellen.
Bis ResizeObserver
gab es keine zuverlässige Möglichkeit, benachrichtigt zu werden, wenn sich die Abmessungen ändern, damit die untergeordneten Elemente neu angeordnet werden können.
Auswirkungen auf „Interaction to Next Paint“ (INP)
Interaction to Next Paint (INP) ist ein Messwert, der die Reaktionsschnelligkeit einer Seite auf Nutzerinteraktionen insgesamt misst. Wenn der INP-Wert einer Seite unter dem Grenzwert für „Gut“ liegt, also 200 Millisekunden oder weniger beträgt, kann davon ausgegangen werden, dass die Seite zuverlässig auf Nutzerinteraktionen reagiert.
Die Zeit, die benötigt wird, bis Ereignis-Callbacks als Reaktion auf eine Nutzerinteraktion ausgeführt werden, kann einen erheblichen Beitrag zur Gesamtlatenz einer Interaktion leisten. Das ist jedoch nicht der einzige Aspekt von INP, der berücksichtigt werden muss. Bei INP wird auch die Zeit berücksichtigt, die bis zum nächsten Paint der Interaktion vergeht. Das ist die Zeit, die benötigt wird, um die Rendering-Aufgaben abzuschließen, die erforderlich sind, um die Benutzeroberfläche als Reaktion auf eine Interaktion zu aktualisieren.
Bei ResizeObserver
ist das wichtig, weil der Callback, der für eine ResizerObserver
-Instanz ausgeführt wird, unmittelbar vor dem Rendern erfolgt. Das ist so gewollt, da die Arbeit, die im Callback erfolgt, berücksichtigt werden muss, da das Ergebnis dieser Arbeit sehr wahrscheinlich eine Änderung der Benutzeroberfläche erfordert.
Achten Sie darauf, in einem ResizeObserver
-Callback so wenig Rendering-Arbeit wie nötig zu erledigen, da übermäßige Rendering-Arbeit dazu führen kann, dass der Browser wichtige Aufgaben verzögert. Wenn beispielsweise für eine Interaktion ein Callback vorhanden ist, der einen ResizeObserver
-Callback auslöst, sollten Sie Folgendes tun, um eine möglichst reibungslose Nutzererfahrung zu ermöglichen:
- Achten Sie darauf, dass Ihre CSS-Selektoren so einfach wie möglich sind, um unnötige Neuberechnungen von Formatierungen zu vermeiden. Stilneuberechnungen erfolgen kurz vor dem Layout. Komplexe CSS-Selektoren können Layoutvorgänge verzögern.
- Vermeiden Sie es, im
ResizeObserver
-Callback Arbeiten auszuführen, die erzwungene Reflows auslösen können. - Die Zeit, die zum Aktualisieren des Layouts einer Seite benötigt wird, steigt in der Regel mit der Anzahl der DOM-Elemente auf einer Seite. Das gilt unabhängig davon, ob auf den Seiten
ResizeObserver
verwendet wird. Die Arbeit, die in einemResizeObserver
-Callback ausgeführt wird, kann jedoch mit zunehmender struktureller Komplexität einer Seite erheblich zunehmen.
Fazit
ResizeObserver
ist in allen wichtigen Browsern verfügbar und bietet eine effiziente Möglichkeit, die Größenänderung von Elementen auf Elementebene zu überwachen. Achten Sie jedoch darauf, das Rendern mit dieser leistungsstarken API nicht zu sehr zu verzögern.