ResizeObserver: Das ist wie „document.onresize“ für Elemente.

ResizeObserver informiert Sie, wenn sich die Größe eines Elements ändert.

Vor dem ResizeObserver mussten Sie einen Listener an das Ereignis resize des Dokuments anhängen, um bei jeder Änderung der Abmessungen des Darstellungsbereichs benachrichtigt zu werden. Im Event-Handler müssen Sie dann herausfinden, welche Elemente von dieser Änderung betroffen sind, und eine bestimmte Routine aufrufen, um entsprechend zu reagieren. Wenn Sie die neuen Abmessungen eines Elements nach einer Größenänderung benötigen, mussten Sie getBoundingClientRect() oder getComputedStyle() aufrufen. Dies kann zu Layout-Überschreitungen führen, wenn Sie nicht alle Lesevorgänge und alle Schreibvorgänge im Batch zusammenfassen müssen.

Dies umfasste nicht einmal Fälle, in denen die Größe von Elementen geändert wurde, ohne dass die Größe des Hauptfensters angepasst worden war. Beispielsweise kann durch das Anfügen neuer untergeordneter Elemente, das Festlegen des Stils display eines Elements auf none oder ähnliche Aktionen die Größe eines Elements sowie dessen gleichgeordneter oder übergeordneter Elemente geändert werden.

Aus diesem Grund ist ResizeObserver eine nützliche Primitive. Sie reagiert auf Größenänderungen eines der beobachteten Elemente, unabhängig davon, was die Änderung verursacht hat. Sie bietet auch Zugriff auf die neue Größe der beobachteten Elemente.

Unterstützte Browser

  • 64
  • 79
  • 69
  • 13.1

Quelle

API

Alle APIs mit dem oben erwähnten Suffix Observer haben ein einfaches API-Design. ResizeObserver ist keine Ausnahme. Sie erstellen ein ResizeObserver-Objekt und übergeben einen Callback an den Konstruktor. An den Callback wird ein Array mit 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 meldet ein ResizeObserverEntry das Inhaltsfeld eines Elements über eine Property namens contentRect, die ein DOMRectReadOnly-Objekt zurückgibt. Das Inhaltsfeld ist das Feld, in das der Inhalt eingefügt werden kann. Es ist das Rahmenfeld ohne den Abstand.

Ein Diagramm des CSS-Box-Modells.

Wichtig: Während ResizeObserver sowohl die Abmessungen von contentRect als auch den Abstand meldet, wird nur contentRect überwacht. contentRect darf nicht mit dem Begrenzungsrahmen des Elements verwechselt werden. Der von getBoundingClientRect() gemeldete Begrenzungsrahmen ist der Rahmen, der das gesamte Element und seine Nachfolger enthält. SVGs sind eine Ausnahme von der Regel, bei der ResizeObserver die Abmessungen des Begrenzungsrahmens angibt.

Ab Chrome 84 hat ResizeObserverEntry drei neue Eigenschaften, um detailliertere Informationen zu erhalten. Jedes dieser Attribute gibt ein ResizeObserverSize-Objekt zurück, das ein blockSize-Attribut und ein inlineSize-Attribut enthält. Diese Informationen beziehen sich auf das beobachtete Element zum Zeitpunkt des Callbacks.

  • borderBoxSize
  • contentBoxSize
  • devicePixelContentBoxSize

Alle diese Elemente geben schreibgeschützte Arrays zurück, da sie in Zukunft Elemente mit mehreren Fragmenten unterstützen können, die in mehrspaltigen Szenarien vorkommen. Derzeit enthalten diese Arrays nur ein Element.

Die Plattformunterstützung für diese Eigenschaften ist eingeschränkt, aber Firefox unterstützt bereits die ersten beiden Eigenschaften.

Wann wird es gemeldet?

Laut Spezifikation muss ResizeObserver alle Größenänderungsereignisse vor dem Painting und nach dem Layout verarbeiten. Das macht den Callback eines ResizeObserver-Objekts ideal, um das Layout deiner Seite zu ändern. Da die ResizeObserver-Verarbeitung zwischen Layout und Paint erfolgt, wird damit nur das Layout ungültig und nicht das Painting.

Verstanden

Sie fragen sich vielleicht, was passiert, wenn ich die Größe eines beobachteten Elements im Callback zu ResizeObserver ändere? Die Antwort lautet: Sie lösen sofort einen weiteren Aufruf an den Callback aus. ResizeObserver verfügt über einen Mechanismus, der unendliche Callback-Schleifen und zyklische Abhängigkeiten verhindert. Änderungen werden nur dann im selben Frame verarbeitet, wenn das geänderte Element in der DOM-Baumstruktur tiefer ist als das oberste Element, das im vorherigen Callback verarbeitet wurde. Andernfalls werden sie auf den nächsten Frame zurückgestellt.

Anwendung

Mit ResizeObserver können Sie u. a. Medienabfragen pro Element implementieren. Durch das Beobachten von Elementen können Sie unbedingt Designhaltepunkte definieren und die Stile eines Elements ändern. Im folgenden Beispiel ändert das zweite Feld den Rahmenradius 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 Chat-Fenster. Das Problem, das bei einem typischen Layout von Top-unten-Unterhaltungen auftritt, ist die Scrollposition. Um Nutzer nicht zu verwirren, ist es hilfreich, das Fenster am unteren Rand der Unterhaltung zu fixieren, wo die neuesten Nachrichten angezeigt werden. Darüber hinaus sollte jede Art von Layoutänderung (ein Smartphone, das von Querformat in Hochformat oder umgekehrt wechselt) dasselbe erreichen.

Mit ResizeObserver können Sie ein einzelnes Code-Snippet schreiben, das beide Szenarien abdeckt. Die Größenanpassung des Fensters ist ein Ereignis, das ein ResizeObserver per Definition erfassen kann. Durch das Aufrufen von appendChild() wird die Größe des Elements aber auch geändert (es sei denn, overflow: hidden ist festgelegt), da Platz für die neuen Elemente erforderlich ist. Vor diesem Hintergrund braucht es nur sehr wenige Zeilen, um den gewünschten Effekt zu erzielen:

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);

Ganz schön praktisch, oder?

Von hier aus könnte ich weiteren Code hinzufügen, um den Fall zu beheben, dass der Nutzer manuell nach oben gescrollt hat und beim Eintreffen einer neuen Nachricht das Scrollen bei dieser Nachricht beibehalten möchte.

Ein weiterer Anwendungsfall ist für jede Art von benutzerdefinierten Elementen, die ein eigenes Layout haben. Bis zum ResizeObserver gab es keine zuverlässige Möglichkeit, Benachrichtigungen zu erhalten, wenn sich die Abmessungen ändern, damit die untergeordneten Elemente neu angelegt werden können.

Auswirkungen auf die Interaktion mit Next Paint (INP)

Interaction to Next Paint (INP) ist ein Messwert, der die Reaktionsfähigkeit einer Seite auf Nutzerinteraktionen insgesamt misst. Wenn der INP-Wert einer Seite im Grenzwert für „gut“ liegt, also 200 Millisekunden oder weniger, kann dies bedeuten, dass die Seite zuverlässig auf die Interaktionen des Nutzers mit ihr reagiert.

Auch wenn die Zeit, die für die Ausführung von Ereignis-Callbacks als Reaktion auf eine Nutzerinteraktion benötigt wird, erheblich zur Gesamtlatenz einer Interaktion beitragen kann, ist dies nicht der einzige Aspekt von INP, den es zu berücksichtigen gilt. INP berücksichtigt auch die Zeit, die benötigt wird, bis der nächste Paint der Interaktion erfolgt. Dies ist die Zeit, die für die Aktualisierung der Benutzeroberfläche als Reaktion auf eine Interaktion benötigt wird.

Bei ResizeObserver ist dies wichtig, da der Callback, dass eine ResizerObserver-Instanz ausgeführt wird, direkt vor dem Rendering erfolgt. Dies ist so konzipiert, dass die im Callback durchgeführte Arbeit berücksichtigt werden muss. Als Ergebnis dieser Arbeit ist daher höchstwahrscheinlich eine Änderung der Benutzeroberfläche erforderlich.

Erledigen Sie so wenig Renderingarbeiten wie in einem ResizeObserver-Callback erforderlich, da ein übermäßiger Rendering-Aufwand Situationen zur Folge haben kann, dass der Browser wichtige Aufgaben verzögert. Wenn beispielsweise eine Interaktion einen Callback hat, der die Ausführung eines ResizeObserver-Callbacks auslöst, solltest du Folgendes tun, um für einen möglichst reibungslosen Ablauf zu sorgen:

  • Achten Sie darauf, dass Ihre CSS-Selektoren so einfach wie möglich sind, um eine übermäßige Neuberechnung von Stilen zu vermeiden. Stilneuberechnungen erfolgen direkt vor dem Layout und komplexe CSS-Selektoren können Layoutvorgänge verzögern.
  • Vermeiden Sie Aktionen im ResizeObserver-Callback, die erzwungene Umbrüche auslösen können.
  • Die erforderliche Zeit für die Aktualisierung des Seitenlayouts steigt in der Regel mit der Anzahl der DOM-Elemente auf einer Seite. Dies gilt unabhängig davon, ob Seiten ResizeObserver verwenden oder nicht. Die in einem ResizeObserver-Callback erledigte Arbeit kann jedoch beträchtlich sein, wenn die strukturelle Komplexität einer Seite zunimmt.

Fazit

ResizeObserver ist in allen gängigen Browsern verfügbar und bietet eine effiziente Möglichkeit, um die Größe von Elementen auf Elementebene zu überwachen. Aber seien Sie vorsichtig, um das Rendering mit dieser leistungsstarken API nicht zu stark zu verzögern.