Start · Sprachen · JavaScript · Referenz · Document: visibilitychange

Document: visibilitychange

Ereignis

Wird am <code>document</code> ausgelöst, wenn die Seite sichtbar oder verborgen wird, z. B. beim Tab-Wechsel, Minimieren oder Verlassen der Seite.

seit JavaScript Baseline: alle modernen Browser (seit 2015 weit ve Kategorie: webapi

Signatur

document.addEventListener("visibilitychange", handler)

Beschreibung

Das Ereignis visibilitychange gehört zur Page Visibility API. Der Browser löst es am document aus, sobald sich document.visibilityState zwischen "visible" und "hidden" ändert. Das passiert etwa, wenn der Benutzer den Tab wechselt, das Browserfenster minimiert, den Bildschirm sperrt, auf dem Handy zu einer anderen App wechselt oder die Seite verlässt bzw. schließt.

Einen Handler registriert man mit document.addEventListener("visibilitychange", handler) oder über die Eigenschaft document.onvisibilitychange. Das Ereignis ist ein einfaches Event ohne Zusatzdaten, bubbelt nicht und lässt sich nicht abbrechen. Den aktuellen Zustand liest man im Handler über document.visibilityState bzw. document.hidden aus.

Typische Einsatzzwecke: Videos oder Animationen anhalten, Polling und Timer drosseln, ungespeicherte Daten sichern und Analyse- oder Sitzungsdaten zuverlässig senden. Der Wechsel nach "hidden" ist auf Mobilgeräten oft das letzte Ereignis, das eine Seite zuverlässig erhält. Deshalb gilt er als empfohlener Ersatz für unload und beforeunload, wenn es ums Speichern oder Senden von Daten geht.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "visibilitychange".
$handler Pflicht (event: Event) => void Callback, der bei jeder Änderung der Sichtbarkeit aufgerufen wird. Er bekommt ein einfaches Event-Objekt; den Zustand liest man aus document.visibilityState.
$options boolean | AddEventListenerOptions false Optionen wie once, passive, capture oder signal (zum Entfernen per AbortController).

Rückgabewert

Typ
void
Beschreibung
addEventListener hat keinen Rückgabewert. Der Handler erhält ein Event, das nicht bubbelt und nicht abbrechbar ist (cancelable: false).

Beispiele

Video beim Tab-Wechsel pausieren (addEventListener)

const video = document.querySelector('video');

const onVisibilityChange = () => {
  if (document.visibilityState === 'hidden') {
    video.pause();
  } else if (video.dataset.autoResume === 'true') {
    video.play();
  }
  console.log('Sichtbarkeit:', document.visibilityState);
};

document.addEventListener('visibilitychange', onVisibilityChange);
Sichtbarkeit: hidden Sichtbarkeit: visible

Analysedaten zuverlässig beim Verlassen senden

const sessionStart = performance.now();

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState !== 'hidden') return;

  const payload = JSON.stringify({
    page: location.pathname,
    duration: Math.round(performance.now() - sessionStart)
  });
  // sendBeacon übersteht das Schließen der Seite
  navigator.sendBeacon('/api/analytics', payload);
});

Polling pausieren und Listener wieder entfernen

let timerId = null;

const poll = async () => {
  const res = await fetch('/api/notifications');
  console.log('Neue Meldungen:', (await res.json()).length);
};

const start = () => { timerId ??= setInterval(poll, 10_000); };
const stop = () => { clearInterval(timerId); timerId = null; };

const handler = () => (document.hidden ? stop() : start());

document.addEventListener('visibilitychange', handler);
start();

// Später, z. B. beim Abbau einer Komponente:
const cleanup = () => {
  document.removeEventListener('visibilitychange', handler);
  stop();
};
Neue Meldungen: 3

Variante über die on-Property

document.onvisibilitychange = () => {
  document.title = document.hidden ? '💤 Komm zurück!' : 'Meine App';
};

// Entfernen:
// document.onvisibilitychange = null;

// Wichtig · Fallstricke

  • Am document registrieren, nicht am window: Das Ereignis wird am document ausgelöst und bubbelt nicht. window.addEventListener('visibilitychange', …) funktioniert zwar in manchen Browsern, weil Listener mit capture auf window es auf dem Weg abfangen, verlassen sollte man sich darauf aber nicht.
  • Ersatz für unload/beforeunload: Auf Mobilgeräten werden unload-Ereignisse oft gar nicht ausgelöst, etwa wenn der Benutzer die App wechselt und das System den Tab später beendet. Daten sichert man daher am besten beim Wechsel nach "hidden", am besten mit navigator.sendBeacon() oder fetch(…, { keepalive: true }). Unload-Handler verhindern außerdem den Back/Forward-Cache.
  • Kein garantiertes Paar: Nach "hidden" folgt nicht unbedingt noch ein "visible", denn die Seite kann danach ohne weitere Ereignisse beendet werden. Auch beim ersten Laden wird visibilitychange nicht ausgelöst. Den Anfangszustand prüft man deshalb direkt über document.visibilityState.
  • Nicht gleich Fokus: Eine Seite kann sichtbar sein, ohne den Fokus zu haben, z. B. in einem zweiten Fenster daneben. Für den Fokus sind focus/blur zuständig. Ein teilweise verdecktes Fenster gilt meist weiterhin als "visible".
  • Veralteter Wert "prerender": Frühere Fassungen der Spezifikation kannten den Zustand "prerender". Er ist entfernt worden. Heute gibt es nur noch "visible" und "hidden". Präfixversionen (webkitvisibilitychange) braucht man nur für sehr alte Browser.
  • Performance: In verborgenen Tabs drosseln Browser Timer ohnehin stark, und requestAnimationFrame läuft dort gar nicht. Der Handler sollte schnell arbeiten, weil nach "hidden" möglicherweise nur noch wenig Rechenzeit bleibt.