Start · Sprachen · JavaScript · Referenz · VisualViewport: scroll

VisualViewport: scroll

Ereignis

Wird ausgelöst, wenn sich der sichtbare Ausschnitt (visuelles Viewport) verschiebt, z. B. beim Scrollen innerhalb einer per Pinch-Zoom vergrößerten Seite.

seit JavaScript Baseline 2021: Chrome 62+, Edge 79+, Firefox 91+, Kategorie: webapi

Signatur

window.visualViewport.addEventListener("scroll", handler)

Beschreibung

Das scroll-Ereignis der Schnittstelle VisualViewport wird ausgelöst, sobald sich der sichtbare Ausschnitt gegenüber dem Layout-Viewport verschiebt. Die Werte offsetLeft/offsetTop bzw. pageLeft/pageTop ändern sich dabei. Typisch ist das auf Mobilgeräten: Zoomt man per Pinch-Geste hinein und wischt dann, bewegt sich nur das visuelle Viewport, das Layout-Viewport bleibt, wo es ist. Deshalb feuert dann oft kein window-scroll-Ereignis.

Man registriert den Handler mit window.visualViewport.addEventListener("scroll", handler) oder über die Eigenschaft window.visualViewport.onscroll = handler. Das Ereignis ist ein einfaches Event: Es ist nicht abbrechbar und steigt nicht auf (kein Bubbling). Die aktuellen Werte liest man direkt aus event.target bzw. window.visualViewport aus.

Wofür man es braucht: Elemente sollen trotz Zoom sichtbar bleiben, z. B. fixierte Werkzeugleisten, Kontextmenüs oder eigene Tooltips. Auch Leisten über der Bildschirmtastatur lassen sich damit ausrichten. Meist wird das Ereignis zusammen mit dem resize-Ereignis von VisualViewport verwendet, weil Zoomen und das Ein- oder Ausblenden der Tastatur die Größe verändern und nicht die Position.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignistyp, hier immer "scroll".
$handler Pflicht (event: Event) => void Callback, der bei jeder Verschiebung aufgerufen wird. event.target ist das VisualViewport-Objekt.
$options boolean | AddEventListenerOptions false Optionen wie passive, once oder signal (zum Entfernen per AbortController).

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Der Handler erhält ein Event-Objekt ohne zusätzliche Eigenschaften.

Beispiele

Scroll-Position des visuellen Viewports protokollieren

const vv = window.visualViewport;

if (vv) {
  vv.addEventListener('scroll', (event) => {
    const { offsetLeft, offsetTop, pageLeft, pageTop, scale } = event.target;
    console.log(`Offset: ${offsetLeft.toFixed(0)}/${offsetTop.toFixed(0)}, ` +
      `Seite: ${pageLeft.toFixed(0)}/${pageTop.toFixed(0)}, Zoom: ${scale.toFixed(2)}`);
  });
}
Offset: 120/340, Seite: 120/1540, Zoom: 2.00

Werkzeugleiste trotz Pinch-Zoom sichtbar halten (scroll + resize)

const toolbar = document.querySelector('#toolbar');
const vv = window.visualViewport;
let pending = false;

const updateToolbar = () => {
  if (pending) return;
  pending = true;
  requestAnimationFrame(() => {
    pending = false;
    // Leiste unten im sichtbaren Bereich ausrichten und Zoom ausgleichen
    const x = vv.offsetLeft;
    const y = vv.offsetTop + vv.height - toolbar.offsetHeight / vv.scale;
    toolbar.style.transform = `translate(${x}px, ${y}px) scale(${1 / vv.scale})`;
    toolbar.style.transformOrigin = '0 0';
  });
};

if (vv) {
  vv.addEventListener('scroll', updateToolbar);
  vv.addEventListener('resize', updateToolbar);
  updateToolbar();
}

on-Property verwenden und Listener wieder entfernen

const vv = window.visualViewport;

// Variante 1: Event-Handler-Eigenschaft
vv.onscroll = () => console.log('pageTop:', vv.pageTop);
// Entfernen:
vv.onscroll = null;

// Variante 2: benannte Funktion und removeEventListener
const onVvScroll = () => console.log('offsetTop:', vv.offsetTop);
vv.addEventListener('scroll', onVvScroll);
vv.removeEventListener('scroll', onVvScroll);

// Variante 3: mehrere Listener über AbortController entfernen
const controller = new AbortController();
vv.addEventListener('scroll', onVvScroll, { signal: controller.signal });
vv.addEventListener('resize', onVvScroll, { signal: controller.signal });
controller.abort(); // entfernt beide Listener

// Wichtig · Fallstricke

  • Nicht gleich window-scroll: Das window-Ereignis bezieht sich auf das Layout-Viewport. Scrollen innerhalb einer gezoomten Ansicht löst oft nur das VisualViewport-scroll aus. Umgekehrt feuern bei normalem Scrollen je nach Browser beide Ereignisse oder nur eines. Wer beide Fälle abdecken will, sollte auf beide hören.
  • Größenänderungen: Beim Zoomen und beim Ein- oder Ausblenden der Bildschirmtastatur ändert sich vor allem die Größe. Dafür gibt es das resize-Ereignis, das man fast immer zusätzlich registriert.
  • Leistung: Das Ereignis kann sehr oft feuern. Schreibzugriffe aufs DOM daher mit requestAnimationFrame bündeln, möglichst mit transform arbeiten und im Handler nicht abwechselnd lesen und schreiben (sonst entsteht Layout-Thrashing).
  • Kein Bubbling, nicht abbrechbar: preventDefault() hat keine Wirkung. Das Scrollen lässt sich damit nicht verhindern.
  • Kompatibilität: window.visualViewport kann in älteren Browsern undefined sein (Firefox vor Version 91; auf Android gab es sie schon früher). Vor dem Zugriff also prüfen, z. B. mit if (window.visualViewport). Auf Desktop-Browsern ohne Pinch-Zoom feuert das Ereignis selten.
  • In Iframes beschreibt visualViewport das Viewport des Iframes.