Start · Sprachen · JavaScript · Referenz · ServiceWorkerGlobalScope: push

ServiceWorkerGlobalScope: push

Ereignis

Wird im <code>ServiceWorkerGlobalScope</code> ausgelöst, wenn der Service Worker über den Push-Dienst des Browsers eine Push-Nachricht vom Server erhält.

seit JavaScript Baseline 2023: Chrome 42+, Firefox 44+, Edge 17+, Kategorie: webapi

Signatur

self.addEventListener("push", (event) => { … })

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

Typ
void
Beschreibung
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"
    })
  );
});
Das System zeigt eine Benachrichtigung mit Titel und Text aus der Push-Nachricht an.

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 })
  );
};
Push empfangen: Server-Deployment abgeschlossen

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));
{ type: "PUSH", data: { title: "Chat", body: "Hallo!" } }

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);
Abonniert: https://fcm.googleapis.com/fcm/send/...

// Wichtig · Fallstricke

  • Immer event.waitUntil() verwenden: Ohne diesen Aufruf kann der Browser den Service Worker beenden, bevor showNotification() 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.data kann null sein, etwa bei Pushes ohne Nutzlast. .json() wirft bei ungültigem JSON einen Fehler, deshalb in try/catch einschließ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 bei postMessage → 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.