Signatur
Beschreibung
Das Event pushsubscriptionchange wird am ServiceWorkerGlobalScope ausgelöst, wenn sich ein Push-Abonnement ändert, ohne dass die Anwendung das veranlasst hat. Typische Gründe sind ein abgelaufenes Abonnement (expirationTime erreicht), eine vom Push-Dienst erneuerte Verbindung oder ein vom Browser widerrufenes Abonnement. Ohne Reaktion darauf sendet der Server Nachrichten an einen Endpunkt, der nicht mehr gültig ist, und Push-Benachrichtigungen kommen nicht mehr an.
Einen Handler registrierst du per self.addEventListener("pushsubscriptionchange", handler) oder über die Eigenschaft self.onpushsubscriptionchange. Das Event-Objekt ist laut Spezifikation ein PushSubscriptionChangeEvent (erweitert ExtendableEvent) mit den Eigenschaften oldSubscription und newSubscription. Beide können null sein, z. B. wenn der Browser das alte Abonnement verworfen und noch kein neues angelegt hat.
Meist sieht der Ablauf so aus: Du legst über self.registration.pushManager.subscribe() ein neues Abonnement an, schickst es an deinen Anwendungsserver und entfernst dort den alten Endpunkt. Diese asynchrone Arbeit muss in event.waitUntil() eingeschlossen werden, sonst kann der Browser den Service Worker vorher beenden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Event-Name "pushsubscriptionchange". |
|
| $handler Pflicht | (event: PushSubscriptionChangeEvent) => void | Callback, der das Event-Objekt erhält. Wichtige Mitglieder: event.oldSubscription (PushSubscription | null), event.newSubscription (PushSubscription | null) und event.waitUntil(promise). |
|
| $options | boolean | AddEventListenerOptions | false | Optionale Listener-Optionen wie bei addEventListener (z. B. { once: true }). |
Rückgabewert
addEventListener hat keinen Rückgabewert. Wenn du Arbeit in event.waitUntil() übergibst, bleibt der Service Worker aktiv, bis das Promise erfüllt oder abgelehnt ist.Beispiele
Neu abonnieren und den Server informieren (addEventListener)
// sw.js
const VAPID_PUBLIC_KEY = 'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U';
// Base64url-String in ein Uint8Array umwandeln
const urlBase64ToUint8Array = (base64) => {
const padding = '='.repeat((4 - (base64.length % 4)) % 4);
const raw = atob((base64 + padding).replace(/-/g, '+').replace(/_/g, '/'));
return Uint8Array.from(raw, (c) => c.charCodeAt(0));
};
self.addEventListener('pushsubscriptionchange', (event) => {
const resubscribe = async () => {
// Neues Abonnement verwenden, wenn der Browser schon eines angelegt hat
const subscription = event.newSubscription ??
await self.registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey:
event.oldSubscription?.options?.applicationServerKey ??
urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
await fetch('/api/push/subscription', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
oldEndpoint: event.oldSubscription?.endpoint ?? null,
subscription: subscription.toJSON()
})
});
console.log('Push-Abonnement erneuert:', subscription.endpoint);
};
event.waitUntil(resubscribe());
});
Handler über die on-Property setzen
// sw.js
self.onpushsubscriptionchange = (event) => {
event.waitUntil(
fetch('/api/push/unsubscribe', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ endpoint: event.oldSubscription?.endpoint ?? null })
}).then(() => console.log('Alter Endpunkt wurde auf dem Server entfernt'))
);
};
Listener wieder entfernen
// sw.js
const onSubscriptionChange = (event) => {
console.log('Änderung erkannt, altes Abo:', event.oldSubscription?.endpoint);
};
self.addEventListener('pushsubscriptionchange', onSubscriptionChange);
// Später, z. B. nach einer Konfigurationsänderung:
self.removeEventListener('pushsubscriptionchange', onSubscriptionChange);
console.log('Listener entfernt');
Fallback ohne Event: Abonnement beim Seitenstart prüfen
// main.js – für Browser, die das Event nicht auslösen
const syncSubscription = async () => {
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.getSubscription();
if (!subscription) {
console.log('Kein Abonnement mehr – Nutzer neu abonnieren');
return;
}
await fetch('/api/push/subscription', {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ subscription: subscription.toJSON() })
});
console.log('Abonnement mit Server abgeglichen');
};
syncSubscription();
// Wichtig · Fallstricke
- Browser-Unterstützung: Nur Firefox löst das Event zuverlässig aus. Chromium-Browser und Safari lösen es praktisch nicht aus. Verlass dich deshalb nicht allein darauf: Prüfe das Abonnement zusätzlich bei jedem Seitenaufruf mit
pushManager.getSubscription()und gleiche es mit dem Server ab. oldSubscription/newSubscriptionkönnennullsein oder in älteren Implementierungen fehlen. Nutze darum optionale Verkettung (?.) und halte den VAPID-Schlüssel als Rückfallwert bereit.- Kein Nutzerkontext: Das Event kann auftreten, während keine Seite offen ist. Ist die Benachrichtigungs-Berechtigung widerrufen worden, schlägt
subscribe()fehl. Fang den Fehler ab und entferne den Endpunkt auf dem Server. waitUntil()nicht vergessen: Ohne diesen Aufruf kann der Browser den Service Worker beenden, bevorfetchfertig ist.- Listener müssen synchron bei der ersten Auswertung des Service-Worker-Skripts registriert werden, nicht erst in einem Promise oder
setTimeout. - Sicherheit: Der Server-Endpunkt, der Abonnements aktualisiert, sollte die Zuordnung zum Nutzer prüfen, z. B. über Cookies oder ein Token. Sonst könnten Dritte fremde Endpunkte ersetzen. Push funktioniert nur in sicheren Kontexten (HTTPS).