Start · Sprachen · JavaScript · Referenz · IntersectionObserver

IntersectionObserver

Schnittstelle

Beobachtet asynchron, ob und wie stark ein Ziel-Element den Viewport oder ein übergeordnetes Scroll-Element überschneidet.

seit JavaScript Baseline: alle modernen Browser (seit 2019) Kategorie: webapi

Signatur

interface IntersectionObserver

Beschreibung

Das Interface IntersectionObserver der Intersection Observer API meldet asynchron, wenn sich die Überschneidung (Intersection) eines Ziel-Elements mit einem Wurzel-Element (root) ändert. Die Wurzel ist entweder der Viewport des Dokuments auf oberster Ebene oder ein übergeordnetes, scrollbares Element. Der Browser ruft Ihre Callback-Funktion nur auf, wenn eine der konfigurierten Schwellen (threshold) überschritten wird. So müssen Sie scroll-Events nicht selbst auswerten und getBoundingClientRect() nicht ständig aufrufen.

Typische Einsatzfälle:

  • Lazy Loading von Bildern, iframes oder Komponenten, sobald sie in die Nähe des Viewports kommen
  • Endloses Scrollen (Infinite Scroll) mit einem Sentinel-Element am Listenende
  • Sichtbarkeits-Tracking (z. B. Werbeeinblendungen, Lesefortschritt, Analytics)
  • Scroll-Animationen, die starten, sobald ein Abschnitt sichtbar wird
  • Pausieren von Videos oder Animationen außerhalb des sichtbaren Bereichs

Sie erzeugen einen Observer mit new IntersectionObserver(callback, options) und registrieren Ziele mit observe(element). Ein einziger Observer kann beliebig viele Elemente beobachten. Die Callback-Funktion bekommt ein Array von IntersectionObserverEntry-Objekten mit Eigenschaften wie isIntersecting, intersectionRatio, boundingClientRect und target. Mit unobserve() beenden Sie die Beobachtung eines einzelnen Elements, mit disconnect() die aller Elemente. takeRecords() liefert noch nicht zugestellte Einträge sofort.

Schreibgeschützte Eigenschaften der Instanz: root, rootMargin, thresholds (immer ein sortiertes Array) sowie in neueren Browsern delay und trackVisibility (Intersection Observer v2).

Parameter

Name Typ Default Beschreibung
$callback Pflicht (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => void Wird aufgerufen, wenn sich der Überschneidungsstatus eines beobachteten Elements über eine Schwelle hinweg ändert, und einmal direkt nach observe() mit dem Anfangszustand.
$options object {} Konfigurationsobjekt mit den folgenden Feldern.
$options.root Element | Document | null null Wurzel-Element, gegen das die Überschneidung berechnet wird. Muss ein Vorfahre der Ziele sein. null steht für den Viewport des Dokuments auf oberster Ebene.
$options.rootMargin string "0px 0px 0px 0px" Vergrößert oder verkleinert den Wurzelbereich, Syntax wie bei CSS margin (nur px und %). Beispiel: "200px 0px" löst 200 px vor dem Sichtbarwerden aus.
$options.threshold number | number[] 0 Einzelwert oder Array von Werten zwischen 0 und 1. Gibt an, bei welchen sichtbaren Anteilen die Callback-Funktion ausgelöst wird. 0 heißt: sobald ein einziges Pixel sichtbar ist, 1 heißt: vollständig sichtbar.
$options.scrollMargin string "0px" Neuer Standard: Rand um verschachtelte Scroll-Container, damit Elemente in inneren Scroll-Bereichen früher erkannt werden. Wird noch nicht von allen Browsern unterstützt.
$options.trackVisibility boolean false Intersection Observer v2 (nur Chromium): prüft zusätzlich, ob das Element tatsächlich sichtbar ist (nicht verdeckt, nicht transparent). Setzt delay ≥ 100 voraus.
$options.delay number 0 Minimaler Abstand zwischen Benachrichtigungen in Millisekunden (v2, nur Chromium). Pflicht, wenn trackVisibility aktiv ist.

Rückgabewert

Typ
IntersectionObserver
Beschreibung
Eine neue Observer-Instanz. Sie beobachtet erst etwas, nachdem Sie observe() aufgerufen haben. Methoden: observe(target), unobserve(target), disconnect(), takeRecords().

Beispiele

Lazy Loading von Bildern mit Vorlauf

// HTML: <img data-src="foto.jpg" alt="..." class="lazy">
const lazyObserver = new IntersectionObserver((entries, observer) => {
  for (const entry of entries) {
    if (!entry.isIntersecting) continue;
    const img = entry.target;
    img.src = img.dataset.src;
    img.classList.remove('lazy');
    observer.unobserve(img); // nur einmal laden
  }
}, {
  rootMargin: '200px 0px', // 200 px vor dem Sichtbarwerden laden
  threshold: 0
});

document.querySelectorAll('img.lazy').forEach((img) => lazyObserver.observe(img));

Infinite Scroll mit Sentinel-Element

const list = document.querySelector('#liste');
const sentinel = document.querySelector('#sentinel'); // leeres <div> am Ende
let page = 1;
let loading = false;

const loadMore = async () => {
  loading = true;
  const res = await fetch(`/api/artikel?page=${page++}`);
  const items = await res.json();
  if (items.length === 0) {
    infiniteObserver.disconnect(); // keine weiteren Daten
    return;
  }
  for (const item of items) {
    const li = document.createElement('li');
    li.textContent = item.title;
    list.append(li);
  }
  loading = false;
};

const infiniteObserver = new IntersectionObserver(([entry]) => {
  if (entry.isIntersecting && !loading) loadMore();
});

infiniteObserver.observe(sentinel);

Sichtbarkeitsanteil in einem Scroll-Container messen

const container = document.querySelector('.scroll-box');
const box = document.querySelector('.scroll-box .ziel');

const ratioObserver = new IntersectionObserver((entries) => {
  for (const entry of entries) {
    const percent = Math.round(entry.intersectionRatio * 100);
    console.log(`${entry.target.className}: ${percent} % sichtbar`);
    entry.target.style.opacity = entry.intersectionRatio;
  }
}, {
  root: container,
  threshold: Array.from({ length: 11 }, (_, i) => i / 10) // 0, 0.1, … 1
});

ratioObserver.observe(box);
console.log(ratioObserver.thresholds.length);
11 ziel: 0 % sichtbar ziel: 10 % sichtbar …

Scroll-Animation einmalig auslösen

const revealObserver = new IntersectionObserver((entries, obs) => {
  entries
    .filter((e) => e.isIntersecting)
    .forEach((e) => {
      e.target.classList.add('sichtbar'); // CSS-Transition startet
      obs.unobserve(e.target);
    });
}, { threshold: 0.25 });

document.querySelectorAll('.reveal').forEach((el) => revealObserver.observe(el));

// Aufräumen, z. B. beim Verlassen einer SPA-Route
window.addEventListener('pagehide', () => revealObserver.disconnect());