Start · Sprachen · JavaScript · Referenz · ServiceWorkerContainer: controllerchange

ServiceWorkerContainer: controllerchange

Ereignis

Wird ausgelöst, wenn die Seite einen neuen steuernden Service Worker bekommt, also wenn sich <code>navigator.serviceWorker.controller</code> ändert.

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

Signatur

navigator.serviceWorker.addEventListener("controllerchange", handler)

Beschreibung

Das Ereignis controllerchange wird auf dem ServiceWorkerContainer (navigator.serviceWorker) ausgelöst, sobald das Dokument einen neuen steuernden Service Worker erhält. Das passiert zum Beispiel, wenn ein neuer Worker aktiviert wird und über clients.claim() die Kontrolle über bereits offene Seiten übernimmt, oder wenn ein wartender Worker per skipWaiting() den alten ersetzt.

Typischerweise nutzt man das Ereignis für Update-Abläufe in Progressive Web Apps. Der Nutzer bestätigt „Neue Version verfügbar – neu laden?“, die Seite schickt dem wartenden Worker eine Nachricht, damit er skipWaiting() aufruft, und sobald controllerchange eintrifft, lädt die Seite neu. So laufen danach alle Anfragen über den neuen Worker und seinen neuen Cache.

Registrieren kann man den Handler mit navigator.serviceWorker.addEventListener("controllerchange", handler) oder über die Eigenschaft navigator.serviceWorker.oncontrollerchange. Der Handler bekommt ein einfaches Event ohne weitere Daten. Den neuen Worker liest man aus navigator.serviceWorker.controller aus.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "controllerchange".
$handler Pflicht (event: Event) => void Callback-Funktion. Sie erhält ein normales Event; den neuen Controller liest man über navigator.serviceWorker.controller aus.
$options boolean | AddEventListenerOptions false Optionen wie { once: true }, damit der Handler nur einmal läuft. Das ist beim Neuladen der Seite sinnvoll.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Das Ereignisobjekt ist ein einfaches Event ohne zusätzliche Eigenschaften.

Beispiele

Auf Controller-Wechsel reagieren (addEventListener)

if ('serviceWorker' in navigator) {
  navigator.serviceWorker.addEventListener('controllerchange', () => {
    const sw = navigator.serviceWorker.controller;
    console.log('Neuer Controller:', sw?.scriptURL);
  });

  navigator.serviceWorker.register('/sw.js');
}
Neuer Controller: https://example.com/sw.js

Update-Ablauf: skipWaiting auslösen und einmal neu laden

// Seite (main.js)
const reg = await navigator.serviceWorker.register('/sw.js');

const aktualisieren = (worker) => {
  if (confirm('Neue Version verfügbar. Jetzt aktualisieren?')) {
    worker.postMessage({ type: 'SKIP_WAITING' });
  }
};

if (reg.waiting) aktualisieren(reg.waiting);

reg.addEventListener('updatefound', () => {
  const neu = reg.installing;
  neu?.addEventListener('statechange', () => {
    if (neu.state === 'installed' && navigator.serviceWorker.controller) {
      aktualisieren(neu);
    }
  });
});

let neuGeladen = false;
navigator.serviceWorker.addEventListener('controllerchange', () => {
  if (neuGeladen) return; // Endlosschleife verhindern
  neuGeladen = true;
  window.location.reload();
});

// Service Worker (sw.js)
// self.addEventListener('message', (e) => {
//   if (e.data?.type === 'SKIP_WAITING') self.skipWaiting();
// });

Variante mit on-Property und Handler wieder entfernen

const onWechsel = () => {
  console.log('Seite wird jetzt gesteuert von:',
    navigator.serviceWorker.controller?.scriptURL);
};

// Per Eigenschaft (überschreibt einen früheren Handler)
navigator.serviceWorker.oncontrollerchange = onWechsel;

// Später wieder abmelden
navigator.serviceWorker.oncontrollerchange = null;

// Alternativ mit addEventListener / removeEventListener
navigator.serviceWorker.addEventListener('controllerchange', onWechsel);
navigator.serviceWorker.removeEventListener('controllerchange', onWechsel);

// Wichtig · Fallstricke

  • Wird beim ersten Besuch nicht automatisch ausgelöst: Nach der ersten Registrierung steuert der Worker die aktuelle Seite erst nach einem Neuladen. Ruft der Worker aber in seinem activate-Handler clients.claim() auf, feuert controllerchange schon beim ersten Besuch. Wer dann blind neu lädt, lädt beim Erstbesuch unnötig neu.
  • Neulade-Schleifen: Mit den DevTools-Optionen „Update on reload“ oder bei mehrfachem Auslösen kann ein Handler, der location.reload() aufruft, eine Schleife erzeugen. Dagegen hilft ein Merker (siehe Beispiel) oder { once: true }.
  • Controller kann null werden: Wird die Registrierung entfernt (unregister()), ist navigator.serviceWorker.controller danach null. Vor dem Zugriff also prüfen.
  • Alle Tabs sind betroffen: Das Ereignis tritt in jedem Tab auf, den der neue Worker übernimmt. Ein automatisches Neuladen kann in anderen Tabs Formulareingaben verwerfen.
  • Kompatibilität: Wird in allen modernen Browsern unterstützt (Chrome, Edge, Firefox, Safari ab 11.1). Service Worker brauchen einen sicheren Kontext (HTTPS oder localhost). In privaten Fenstern mancher Browser (z. B. Firefox) ist navigator.serviceWorker nicht verfügbar. Vorher mit 'serviceWorker' in navigator prüfen.