Start · Sprachen · JavaScript · Referenz · Clients

Clients

Schnittstelle

Das <code>Clients</code>-Interface gibt einem Service Worker Zugriff auf die von ihm kontrollierten Fenster, Worker und Shared Worker (<code>Client</code>-Objekte).

seit JavaScript Baseline: alle modernen Browser (Service Worker) Kategorie: webapi

Signatur

interface Clients

Beschreibung

Das Clients-Interface gibt es nur im Service-Worker-Kontext. Man erreicht es über self.clients in einem ServiceWorkerGlobalScope. Damit findet, steuert und öffnet ein Service Worker die Clients seines Gültigkeitsbereichs (Scope), also Browser-Tabs, dedizierte Worker und Shared Worker.

Die wichtigsten Methoden:

  • clients.get(id): liefert den Client zu einer ID, z. B. zu event.clientId aus einem fetch-Event.
  • clients.matchAll(options): liefert alle passenden Clients, optional gefiltert nach Typ oder auch unkontrollierte Clients.
  • clients.openWindow(url): öffnet ein neues Fenster oder einen neuen Tab. Das ist z. B. nach einem Klick auf eine Benachrichtigung nötig.
  • clients.claim(): macht den aktiven Service Worker sofort zum Controller aller Clients im Scope, ohne dass die Seite neu geladen werden muss.

Typische Anwendungsfälle sind Push-Benachrichtigungen („Tab fokussieren, falls schon offen, sonst neu öffnen“), Nachrichten an alle offenen Tabs (z. B. „Neue Version verfügbar“) und das sofortige Übernehmen der Kontrolle nach der Aktivierung. Alle Methoden arbeiten asynchron und geben ein Promise zurück.

Parameter

Name Typ Default Beschreibung
$id Pflicht string Nur bei get(id): die eindeutige ID des gesuchten Clients, z. B. event.clientId oder event.resultingClientId.
$options { includeUncontrolled?: boolean, type?: 'window' | 'worker' | 'sharedworker' | 'all' } { includeUncontrolled: false, type: 'window' } Nur bei matchAll(options): includeUncontrolled nimmt auch Clients auf, die nicht von diesem Service Worker kontrolliert werden. type filtert nach Art des Clients.
$url Pflicht string Nur bei openWindow(url): die zu öffnende URL. Sie wird relativ zum Service-Worker-Skript aufgelöst.

Rückgabewert

Typ
Promise<Client | undefined> | Promise<Client[]> | Promise<WindowClient | null> | Promise<void>
Beschreibung
get() liefert einen Client oder undefined. matchAll() liefert ein Array von Client-Objekten. openWindow() liefert einen WindowClient oder null, wenn die URL einen anderen Ursprung hat. claim() liefert ein Promise<void>.

Beispiele

Kontrolle sofort übernehmen mit claim()

// sw.js
self.addEventListener('install', (event) => {
  self.skipWaiting(); // Wartephase überspringen
});

self.addEventListener('activate', (event) => {
  // Alle offenen Seiten im Scope sofort kontrollieren
  event.waitUntil(self.clients.claim());
});

Nachricht an alle offenen Tabs senden

// sw.js
const broadcast = async (message) => {
  const allClients = await self.clients.matchAll({
    type: 'window',
    includeUncontrolled: true
  });
  for (const client of allClients) {
    client.postMessage(message);
  }
  return allClients.length;
};

self.addEventListener('activate', (event) => {
  event.waitUntil(
    broadcast({ type: 'SW_UPDATED', version: '2.1.0' })
      .then((n) => console.log(`${n} Tab(s) benachrichtigt`))
  );
});

// Auf der Seite (main.js):
// navigator.serviceWorker.addEventListener('message', (e) => console.log(e.data));
2 Tab(s) benachrichtigt

Klick auf Benachrichtigung: Tab fokussieren oder öffnen

// sw.js
self.addEventListener('notificationclick', (event) => {
  event.notification.close();
  const targetUrl = new URL('/posteingang', self.location.origin).href;

  event.waitUntil((async () => {
    const windows = await self.clients.matchAll({ type: 'window', includeUncontrolled: true });
    const existing = windows.find((c) => c.url === targetUrl);
    if (existing) {
      return existing.focus();
    }
    return self.clients.openWindow(targetUrl);
  })());
});

Absender eines fetch-Requests ermitteln mit get()

// sw.js
self.addEventListener('fetch', (event) => {
  if (!event.clientId) return; // z. B. Navigationsanfragen

  event.waitUntil((async () => {
    const client = await self.clients.get(event.clientId);
    if (client) {
      console.log(`Anfrage ${event.request.url} von ${client.type}-Client ${client.url}`);
    }
  })());
});
Anfrage https://example.com/api/data von window-Client https://example.com/

// Wichtig · Fallstricke

  • Nur im Service Worker: self.clients gibt es ausschließlich im ServiceWorkerGlobalScope. Auf normalen Seiten oder in dedizierten Workern ist es nicht vorhanden.
  • openWindow() braucht eine Nutzeraktion: Browser erlauben den Aufruf nur im Zusammenhang mit einer Nutzerinteraktion, typischerweise in einem notificationclick-Handler. Sonst wird das Promise mit einem InvalidAccessError abgelehnt. Bei einer URL mit fremdem Ursprung liefert es null.
  • matchAll() findet standardmäßig nur kontrollierte Fenster: Direkt nach der ersten Installation werden offene Tabs noch nicht kontrolliert. Dann includeUncontrolled: true setzen oder clients.claim() aufrufen.
  • claim() mit Bedacht einsetzen: Seiten, die ohne Service Worker geladen wurden, bekommen plötzlich einen Controller. Ab dann gehen ihre Anfragen durch den neuen Service Worker, und controllerchange wird ausgelöst.
  • Aufrufe in Event-Handlern immer in event.waitUntil() einschließen. Sonst beendet der Browser den Service Worker womöglich vor dem Abschluss.
  • Kompatibilität: Wird in allen modernen Browsern (Chrome, Edge, Firefox, Safari 11.1+) unterstützt. Voraussetzung ist ein sicherer Kontext (HTTPS oder localhost). Die Reihenfolge der Ergebnisse von matchAll() (zuletzt fokussierte zuerst) ist nicht in allen Browsern gleich umgesetzt.