Signatur
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
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}`);
});
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();
// 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 documentund sieh einen Fallback vor, z. B. überscrollendoderIntersectionObserver. - Nur beim Wurzel-Scroller: Am
Documentkommt das Ereignis nur an, wenn das Dokument selbst scrollt undscroll-snap-typeaufhtmlgesetzt 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-typeund passendescroll-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:
snapTargetBlockbzw.snapTargetInlineistnull, 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.