Start · Sprachen · JavaScript · Referenz · Document: scrollsnapchange

Document: scrollsnapchange

Ereignis

Wird am Dokument ausgelöst, wenn am Ende eines Scrollvorgangs im Wurzel-Scrollcontainer ein neues Scroll-Snap-Ziel feststeht.

seit JavaScript Experimentell: Chrome/Edge 129+, noch nicht in Fir Kategorie: webapi

Signatur

document.addEventListener("scrollsnapchange", handler)

Beschreibung

Das Ereignis scrollsnapchange gehört zur CSS Scroll Snap-Erweiterung. Am Document wird es ausgelöst, wenn der Wurzel-Scrollcontainer (also das Element <html> mit scroll-snap-type) nach einem Scrollvorgang an einem neuen Snap-Ziel einrastet. Rastet die Seite wieder am selben Element ein, wird das Ereignis nicht ausgelöst.

Der Handler erhält ein SnapEvent. Seine Eigenschaften snapTargetBlock und snapTargetInline verweisen auf das Element, an dem in Block- bzw. Inline-Richtung eingerastet wurde. Gibt es in einer Achse kein Snap-Ziel, ist der Wert null.

Typische Einsatzzwecke sind: die aktive Folie oder den aktiven Abschnitt hervorheben, Navigationspunkte oder die URL aktualisieren, Analyse-Ereignisse senden oder Inhalte erst dann nachladen, wenn der Nutzer tatsächlich an einem Abschnitt stehen bleibt. Früher musste man dafür scroll-Ereignisse mit Positionsberechnung oder einen IntersectionObserver kombinieren.

Anmelden kann man den Handler über addEventListener("scrollsnapchange", …) oder über die Eigenschaft document.onscrollsnapchange. Für Rückmeldung während des Scrollens gibt es das Schwesterereignis scrollsnapchanging.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "scrollsnapchange".
$handler Pflicht (event: SnapEvent) => void Callback, der ein SnapEvent mit snapTargetBlock und snapTargetInline erhält.
$options boolean | AddEventListenerOptions false Optionale Listener-Optionen wie once, passive oder signal.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Der Handler erhält ein SnapEvent; sein Rückgabewert wird ignoriert.

Beispiele

Aktiven Abschnitt nach dem Einrasten hervorheben

/* CSS:
  html { scroll-snap-type: y mandatory; }
  section { height: 100vh; scroll-snap-align: start; }
*/
document.addEventListener("scrollsnapchange", (event) => {
  const target = event.snapTargetBlock;
  if (!target) return;

  document.querySelectorAll("section").forEach((s) =>
    s.classList.toggle("active", s === target)
  );
  console.log(`Eingerastet an: #${target.id}`);
});
Eingerastet an: #kapitel-2

Navigation und URL-Hash per on-Eigenschaft aktualisieren

document.onscrollsnapchange = ({ snapTargetBlock }) => {
  if (!snapTargetBlock?.id) return;

  // Hash ändern, ohne erneut zu scrollen
  history.replaceState(null, "", `#${snapTargetBlock.id}`);

  document.querySelectorAll("nav a").forEach((link) => {
    const isCurrent = link.hash === `#${snapTargetBlock.id}`;
    link.toggleAttribute("aria-current", isCurrent);
  });
};

// Später wieder abmelden:
// document.onscrollsnapchange = null;

Feature-Erkennung mit Fallback und Abmelden über AbortController

const controller = new AbortController();

const onSnap = (event) => {
  console.log("Block:", event.snapTargetBlock?.id ?? null);
  console.log("Inline:", event.snapTargetInline?.id ?? null);
};

if ("onscrollsnapchange" in document) {
  document.addEventListener("scrollsnapchange", onSnap, {
    signal: controller.signal,
  });
} else {
  // Fallback: nach Scrollende selbst ermitteln
  document.addEventListener("scrollend", () => {
    const sections = [...document.querySelectorAll("section")];
    const current = sections.find(
      (s) => Math.abs(s.getBoundingClientRect().top) < 5
    );
    console.log("Fallback-Ziel:", current?.id ?? null);
  }, { signal: controller.signal });
}

// Beide Listener entfernen:
// controller.abort();
Block: kapitel-3 Inline: null

// Wichtig · Fallstricke

  • Browser-Unterstützung: Das Ereignis ist experimentell und bisher nur in Chromium-Browsern (ab Chrome/Edge 129) verfügbar. Prüfe die Unterstützung mit "onscrollsnapchange" in document und sieh einen Fallback vor, z. B. über scrollend oder IntersectionObserver.
  • Nur beim Wurzel-Scroller: Am Document kommt das Ereignis nur an, wenn das Dokument selbst scrollt und scroll-snap-type auf html gesetzt ist. Für eigene Scrollcontainer (z. B. ein Karussell-div) meldest du den Listener direkt am Element an.
  • Kein Snap, kein Ereignis: Ohne scroll-snap-type und passende scroll-snap-align-Kindelemente wird das Ereignis nie ausgelöst. Bleibt das Snap-Ziel gleich, wird es ebenfalls nicht ausgelöst.
  • Zeitpunkt: Das Ereignis wird erst ausgelöst, wenn der Scrollvorgang abgeschlossen ist (inklusive Touch-Fingerbewegung und Scroll-Animation), nicht schon während des Scrollens. Für eine Live-Vorschau eignet sich scrollsnapchanging.
  • null-Werte: snapTargetBlock bzw. snapTargetInline ist null, wenn die jeweilige Achse nicht einrastet. Prüfe den Wert deshalb vor dem Zugriff, z. B. mit optionaler Verkettung (?.).
  • Das Ereignis kann nicht abgebrochen werden. preventDefault() hat keine Wirkung.