Signatur
Beschreibung
Das push-Event ist die Grundlage für Web-Push-Benachrichtigungen. Sendet Ihr Server über das Web Push Protocol eine Nachricht an den Push-Dienst des Browsers (z. B. FCM, Mozilla Autopush, Apple Push Service), weckt der Browser den zugehörigen Service Worker auf und löst in dessen globalem Bereich ein push-Event aus. Das funktioniert auch dann, wenn keine Seite der Anwendung geöffnet ist.
Der Handler bekommt ein PushEvent, das von ExtendableEvent erbt. Über event.data (ein PushMessageData-Objekt oder null) lesen Sie die Nutzlast mit .json(), .text(), .arrayBuffer() oder .blob() aus. Mit event.waitUntil(promise) halten Sie den Service Worker so lange am Leben, bis die asynchrone Arbeit fertig ist, typischerweise self.registration.showNotification().
Registrieren können Sie den Handler per self.addEventListener("push", handler) oder über die Eigenschaft self.onpush = handler. Damit überhaupt Nachrichten ankommen, muss die Seite vorher mit registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey }) ein Abonnement anlegen und dem Server den Endpunkt übermitteln.
Chrome und Safari verlangen, dass jede Push-Nachricht eine sichtbare Benachrichtigung auslöst (userVisibleOnly: true). Stumme Pushes ohne Benachrichtigung werden bestraft: Der Browser zeigt dann eine Standardmeldung an oder entzieht das Abonnement.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "push". |
|
| $listener Pflicht | (event: PushEvent) => void | Die Callback-Funktion. Sie erhält ein PushEvent mit data (PushMessageData | null) und der Methode waitUntil(). |
|
| $options | boolean | AddEventListenerOptions | false | Optionale Listener-Optionen wie once oder signal. Im Service Worker werden sie selten gebraucht. |
Rückgabewert
addEventListener gibt nichts zurück. Die Lebensdauer des Handlers verlängern Sie über event.waitUntil().Beispiele
Benachrichtigung aus JSON-Nutzlast anzeigen
// sw.js
self.addEventListener("push", (event) => {
// Fallback, falls keine oder ungültige Daten mitkommen
let payload = { title: "Neue Nachricht", body: "Sie haben eine neue Mitteilung." };
if (event.data) {
try {
payload = event.data.json();
} catch {
payload.body = event.data.text();
}
}
event.waitUntil(
self.registration.showNotification(payload.title, {
body: payload.body,
icon: "/icons/icon-192.png",
badge: "/icons/badge-72.png",
data: { url: payload.url ?? "/" },
tag: payload.tag ?? "allgemein"
})
);
});
Alternative über die onpush-Eigenschaft
// sw.js
self.onpush = (event) => {
const text = event.data?.text() ?? "Ping ohne Inhalt";
console.log("Push empfangen:", text);
event.waitUntil(
self.registration.showNotification("Status-Update", { body: text })
);
};
Offene Tabs informieren statt Benachrichtigung doppelt zu zeigen
// sw.js
self.addEventListener("push", (event) => {
const data = event.data?.json() ?? {};
event.waitUntil((async () => {
const clientList = await self.clients.matchAll({ type: "window", includeUncontrolled: true });
const focused = clientList.find((c) => c.focused);
if (focused) {
// App ist im Vordergrund: Nachricht direkt an die Seite schicken
focused.postMessage({ type: "PUSH", data });
}
// Chrome verlangt trotzdem eine sichtbare Benachrichtigung
await self.registration.showNotification(data.title ?? "Update", {
body: data.body ?? "",
silent: Boolean(focused)
});
})());
});
// Seite: navigator.serviceWorker.addEventListener("message", (e) => console.log(e.data));
Abonnement auf der Seite anlegen (Voraussetzung)
// main.js – im Fensterkontext
const VAPID_PUBLIC_KEY = "BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U";
const urlBase64ToUint8Array = (base64) => {
const padding = "=".repeat((4 - (base64.length % 4)) % 4);
const raw = atob((base64 + padding).replace(/-/g, "+").replace(/_/g, "/"));
return Uint8Array.from(raw, (ch) => ch.charCodeAt(0));
};
const subscribe = async () => {
const registration = await navigator.serviceWorker.register("/sw.js");
if ((await Notification.requestPermission()) !== "granted") return;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true,
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY)
});
await fetch("/api/push/subscribe", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(subscription)
});
console.log("Abonniert:", subscription.endpoint);
};
document.querySelector("#push-aktivieren").addEventListener("click", subscribe);
// Wichtig · Fallstricke
- Immer
event.waitUntil()verwenden: Ohne diesen Aufruf kann der Browser den Service Worker beenden, bevorshowNotification()fertig ist. Die Benachrichtigung erscheint dann nicht, und Chrome zeigt ersatzweise „Diese Website wurde im Hintergrund aktualisiert“. - Keine stummen Pushes: Chrome, Edge und Safari verlangen
userVisibleOnly: true. Wer wiederholt keine Benachrichtigung anzeigt, riskiert, dass das Abonnement widerrufen wird. event.datakannnullsein, etwa bei Pushes ohne Nutzlast..json()wirft bei ungültigem JSON einen Fehler, deshalb intry/catcheinschließen.- Nutzlast-Größe: Verschlüsselte Nutzlasten sind auf etwa 4 KB begrenzt. Für größere Daten nur eine ID senden und den Inhalt im Handler per
fetch()nachladen. - Sicherheit: Die Nutzlast muss serverseitig nach RFC 8291 (aes128gcm) verschlüsselt und mit VAPID signiert werden. Bibliotheken wie
web-push(Node.js) nehmen Ihnen das ab. Endpunkt-URLs gelten als geheim, weil jeder mit Endpunkt und Schlüsseln an das Abonnement senden kann. Nutzlastinhalte nie ungeprüft in Seiten-HTML einsetzen (XSS-Gefahr beipostMessage→innerHTML). - Kompatibilität: Nur in sicheren Kontexten (HTTPS oder localhost). Safari auf iOS/iPadOS ab 16.4 unterstützt Push nur für Web-Apps, die zum Home-Bildschirm hinzugefügt wurden. Im privaten Modus oder bei deaktivierten Benachrichtigungen kommen keine Events an.
- Der Handler muss beim ersten Auswerten des Skripts auf oberster Ebene registriert werden. Ein Listener, der erst asynchron hinzugefügt wird, empfängt beim Aufwecken des Service Workers keine Events.
- Wird ein Abonnement ungültig, feuert
pushsubscriptionchange. Behandeln Sie es, um das Abonnement neu anzulegen.