Intersection Observer API

Elemente überwachen - ganz ohne Blockierung des Hauptthreads

26.05.2025

javascript
Gregor Wedlich
Gregor Wedlich
Life, the Universe and Everything.
Donate with: Alby

Inhaltsverzeichnis

    JS: Intersection Observer

    Source Code: https://codesandbox.io/p/sandbox/3vcjlj

    Die Intersection Observer API bietet eine moderne und performante Methode, asynchron zu beobachten, wann ein Ziel-Element mit einem anderen Element – oder dem Viewport – interagiert. Diese Interaktion, also die Überschneidung, wird als Intersection bezeichnet.

    Konkret lässt sich damit feststellen, ob ein Element (wie eine Textbox, ein Bild usw.) in den sichtbaren Bereich des Browserfensters (Viewport) eintritt und/oder diesen wieder verlässt. Dieses "andere Element", auch Root-Element genannt, ist standardmäßig der Viewport, kann aber auch ein spezifisches anderes DOM-Element sein, innerhalb dessen die Sichtbarkeit des Zielelements beobachtet werden soll.

    Hauptvorteil: Perfomance

    Der entscheidende Vorteil des Intersection Observers ist seine Performance, die sich aus mehreren Schlüsselaspekten ergibt:

    • Asynchrone Ausführung: Beobachtungen und die Ausführung der zugehörigen Logik finden abseits des Hauptthreads des Browsers statt. Das bedeutet, dass Animationen und andere Interaktionen auf der Seite flüssig bleiben, da der Hauptthread nicht blockiert wird.
    • Deklarativer Ansatz: Anstatt ständig manuell Berechnungen wie mit getBoundingClientRect() in Schleifen durchzuführen und Polling zu betreiben, teilen wir dem Browser deklarativ mit, welche Elemente wir beobachten möchten. Der Browser informiert uns dann ereignisgesteuert, wenn eine relevante Überschneidung stattfindet.
    • Energieeffizienz: Durch die Vermeidung von rechenintensiven Dauerabfragen und die effiziente Nutzung von Browser-Interna wird der Energieverbrauch reduziert. Dies kommt insbesondere Mobilgeräten und deren Akkulaufzeit zugute.

    Vor der Einführung des Intersection Observers waren Entwickler:innen oft auf Methoden wie getBoundingClientRect(), komplexe Schleifenkonstrukte und Event-Handler angewiesen, um die Sichtbarkeit von Elementen zu prüfen. Dieser Ansatz führte häufig zu spürbaren Performance-Problemen und einer ruckelnden Benutzererfahrung.

    Der Intersection Observer ermöglicht es uns also, auf elegante Weise zu erfahren, wann Elemente sichtbar werden oder den sichtbaren Bereich wieder verlassen. Dies ist die Grundlage für viele moderne Web-Features. Typische Anwendungsfälle sind beispielsweise das verzögerte Laden von Bildern (Lazy Loading), das Auslösen von Animationen beim Erreichen des sichtbaren Bereichs oder das Implementieren von Infinite-Scrolling-Funktionalitäten. Wie wir solche Interaktionen mithilfe einer Callback-Funktion im Detail umsetzen, schauen wir uns im folgenden Beispiel genauer an.

    Beispiel Observer

    Wir demonstrieren den Intersection Observer nun anhand einiger vertikal angeordneter Boxen, die erst beim Scrollen in den sichtbaren Bereich gelangen.

    Hier gehts zun source code: https://codesandbox.io/p/sandbox/3vcjlj

    Unsere Boxen:
    1 <div id="app"> 2 <div class="wrapper"> 3 <div class="box"></div> 4 <div class="box"></div> 5 <div class="box"></div> 6 </div> 7 </div>

    Den CSS-Code für das Styling der Boxen lassen wir hier aus. Das vollständige, lauffähige Beispiel inklusive CSS findest du unter diesem Link.

    Wir holen uns alle Boxen und speichern diese in einer Variable namens boxes
    1const boxes = document.querySelectorAll(".box");
    Als nächstes erstellen wir den Observer mit unserer Callback Funktion
    1const observer = new IntersectionObserver((entries) => { 2 console.log(entries); 3});

    Die Funktion, die wir IntersectionObserver übergeben, ist unsere Callback-Funktion. Sie wird immer dann aufgerufen, wenn sich der Überschneidungsstatus eines oder mehrerer beobachteter Elemente ändert. Das Argument entries ist ein Array von IntersectionObserverEntry-Objekten, wobei jedes Objekt Informationen über die Überschneidung eines bestimmten Zielelements enthält.

    Dann rufen wir für jedes unserer Boxen den Observer mit observe() auf und können damit überwachen wann ein element im Viewport landet.
    1for (const box of boxes) { 2 observer.observe(box); 3}

    Unser console.log(entries) wird direkt nach dem Aufruf von observer.observe(box) für jede Box einmal ausgeführt. Der Observer meldet also initial den aktuellen Überschneidungsstatus aller beobachteten Elemente. Wenn unsere Boxen zu Beginn außerhalb des sichtbaren Bereichs sind, liefert dies für jede Box einen Eintrag.

    Intersection-Observer-Entries

    Jedes IntersectionObserverEntry-Objekt in unserem entries-Array enthält nützliche Informationen. Die wichtigsten davon sind:

    • target: Das DOM-Element, dessen Überschneidung sich geändert hat.
    • isIntersecting: Ein Boolean, der true ist, wenn das target-Element den Root (standardmäßig den Viewport) überschneidet, andernfalls false.
    • intersectionRatio: Ein Wert zwischen 0.0 und 1.0, der angibt, welcher Anteil des target-Elements sichtbar ist.
    • boundingClientRect: Gibt die Größe und Position des target-Elements relativ zum Viewport an.
    • rootBounds: Gibt die Größe und Position des Root-Elements an.
    • intersectionRect: Gibt die Größe und Position des sichtbaren Teils des target-Elements an.

    intersection-observer-entries-detail-false

    Für unser einfaches Beispiel konzentrieren wir uns zunächst auf den Key isIntersecting. Wenn unsere Boxen, wie im Beispiel angenommen, initial außerhalb des Viewports liegen, wird isIntersecting für jede Box false sein.

    Sobald wir jetzt aber die erste Box in den sichtbaren Bereich scrollen, wird isIntersecting zu true.

    Intersection Observer isIntersecting true

    Und das ist es auch schon, wir könnten jetzt Animation triggern oder andere events auf unserer Box ausführen. Ein kleines Beispiel wir hängen unseren boxen sobald diese im Viewport sind eine CSS-Klasse an und färben unsere Box grün solange diese im viewport ist.

    1const observer = new IntersectionObserver((entries) => { 2 entries.forEach(entry => { 3 if (entry.isIntersecting) { 4 entry.target.classList.add("is-visible"); 5 } else { 6 entry.target.classList.remove("is-visible"); 7 } 8 }); 9});
    Optionen

    Manchmal möchte mann aber vielleicht nicht den Viewport des Browsers als root element verwenden oder man möchte das eine Animation erst ausgelöst wird wenn ein Element 50% im sichbaren bereich ist. Das lässt sich erreichen in dem wir unserem Observer ein paar Optionen mitgeben.

    1const options = { 2 root: null, // Viewport als Referenz verwenden 3 rootMargin: "0px", // Kein zusätzlicher Rand 4 threshold: 0.5 // Auslösen, wenn 50% sichtbar 5};
    1const observer = new IntersectionObserver((entries) => { 2 console.log(entries); 3}, options); // Wir hängen unsere optionen an den Observer

    Jetzt wird isIntersecting erst zu true wenn unser Element zu 50% im Viewport liegt.

    Fazit:

    Ich hoffe, ich konnte in diesem Beitrag verdeutlichen, dass der Intersection Observer eine gute Weiterentwicklung für die Beobachtung der Sichtbarkeit von Elementen im Browser darstellt. Er bietet eine elegante und vor allem performante Lösung für Aufgaben, die zuvor oft mit komplexen und ressourcenintensiven Methoden bewältigt werden mussten.

    Source: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#a_simple_example

    Comments: