Start · Sprachen · JavaScript · Referenz · Window: message

Window: message

Ereignis

Wird am <code>Window</code> ausgelöst, wenn das Fenster eine Nachricht empfängt, etwa über <code>postMessage()</code> aus einem iframe, Popup oder übergeordneten Fenster.

seit JavaScript Baseline: alle Browser Kategorie: webapi

Signatur

window.addEventListener("message", handler)

Beschreibung

Das message-Ereignis ist die Empfängerseite von Window.postMessage(). Damit können Fenster, iframes und Popups aus unterschiedlichen Origins sicher miteinander kommunizieren. Die Same-Origin-Policy wird dabei nicht umgangen. Beide Seiten müssen der Kommunikation ausdrücklich zustimmen.

Den Handler registrierst du mit window.addEventListener("message", handler) oder über die Eigenschaft window.onmessage = handler. Der Handler bekommt ein MessageEvent. Die wichtigsten Eigenschaften sind data (die übertragenen Daten, per Structured Clone kopiert), origin (Origin des Absenders, z. B. "https://example.com"), source (Referenz auf das sendende Fenster, damit du antworten kannst) und ports (übertragene MessagePort-Objekte).

Typische Einsatzfälle sind eingebettete Widgets (Zahlungsformulare, Chats, Karten), OAuth-Popups, die ein Token an das öffnende Fenster zurückgeben, und Mikro-Frontends in iframes. Das Ereignis wird nicht weitergereicht (bubbelt nicht) und lässt sich nicht abbrechen.

Kann eine eingehende Nachricht nicht deserialisiert werden, wird stattdessen messageerror ausgelöst.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Immer "message".
$handler Pflicht (event: MessageEvent) => void Callback, der ein MessageEvent mit data, origin, source, ports und lastEventId erhält.
$options boolean | AddEventListenerOptions false Optionen wie once, signal (zum Entfernen per AbortController) oder capture.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Der Handler bekommt ein MessageEvent.

Beispiele

Nachrichten aus einem iframe empfangen und die Origin prüfen

// Im übergeordneten Fenster (https://shop.example)
const ERLAUBTE_ORIGIN = 'https://pay.example';

const onMessage = (event) => {
  // 1. Immer die Origin prüfen!
  if (event.origin !== ERLAUBTE_ORIGIN) return;

  // 2. Datenstruktur prüfen
  const { type, payload } = event.data ?? {};
  if (type === 'payment:success') {
    console.log('Zahlung erfolgreich:', payload.orderId);
    // 3. Über event.source antworten
    event.source.postMessage({ type: 'ack' }, event.origin);
  }
};

window.addEventListener('message', onMessage);
Zahlung erfolgreich: A-1042

Gegenstück im iframe: Nachricht an das Elternfenster senden

// Im iframe (https://pay.example)
window.parent.postMessage(
  { type: 'payment:success', payload: { orderId: 'A-1042' } },
  'https://shop.example' // Ziel-Origin explizit angeben, nie '*' bei sensiblen Daten
);

window.onmessage = (event) => {
  if (event.origin === 'https://shop.example' && event.data?.type === 'ack') {
    console.log('Shop hat den Empfang bestätigt');
  }
};
Shop hat den Empfang bestätigt

OAuth-Popup: einmalig auf ein Token warten und den Listener wieder entfernen

const waitForToken = (popup, origin, timeoutMs = 60_000) =>
  new Promise((resolve, reject) => {
    const controller = new AbortController();
    const timer = setTimeout(() => {
      controller.abort();
      reject(new Error('Zeitüberschreitung'));
    }, timeoutMs);

    window.addEventListener('message', (event) => {
      if (event.origin !== origin || event.source !== popup) return;
      if (typeof event.data?.token !== 'string') return;
      clearTimeout(timer);
      controller.abort(); // entfernt den Listener
      resolve(event.data.token);
    }, { signal: controller.signal });
  });

const popup = window.open('https://auth.example/login', 'login', 'width=500,height=600');
const token = await waitForToken(popup, 'https://auth.example');
console.log('Token erhalten:', token.slice(0, 8) + '…');
Token erhalten: eyJhbGci…

Listener mit removeEventListener abmelden

const handler = (event) => {
  if (event.origin !== window.location.origin) return;
  console.log('Nachricht:', event.data);
};

window.addEventListener('message', handler);
window.postMessage('Hallo', window.location.origin);

// Später, z. B. beim Unmount einer Komponente
setTimeout(() => window.removeEventListener('message', handler), 1000);
Nachricht: Hallo

// Wichtig · Fallstricke

Sicherheit (wichtig):

  • Prüfe immer event.origin gegen eine feste Liste erlaubter Origins. Jede beliebige Seite, die eine Referenz auf dein Fenster hat (Opener, einbettende Seite), kann dir Nachrichten schicken. Ohne diese Prüfung ist Cross-Site-Scripting oder das Einschleusen von Aktionen möglich.
  • Verwende keine unsicheren Vergleiche wie origin.includes('example.com') oder startsWith. Diese lassen sich mit https://example.com.attacker.net austricksen. Vergleiche exakt mit ===.
  • Behandle event.data als nicht vertrauenswürdige Eingabe. Füge die Daten niemals ungeprüft in innerHTML, eval(), new Function() oder location.href ein (Gefahr von XSS und javascript:-URLs). Prüfe Typ und Struktur der Daten.
  • Gib beim Senden sensibler Daten immer eine konkrete targetOrigin an und nicht '*'. Sonst kann ein umgeleitetes Fenster die Daten mitlesen.
  • Gleiche zusätzlich event.source mit dem erwarteten Fenster ab (z. B. iframe.contentWindow). So nimmst du nur Nachrichten aus diesem einen Fenster an, auch wenn andere Fenster dieselbe Origin haben.
  • CSP-Direktiven wie frame-ancestors und frame-src legen fest, wer eingebettet werden darf. Die Prüfung der Nachrichten ersetzen sie aber nicht.

Weitere Fallstricke:

  • Browser-Erweiterungen und Tools wie React DevTools oder Webpack HMR senden ebenfalls message-Ereignisse. Filtere deshalb nach einem eigenen type-Feld.
  • Die Daten werden per Structured Clone kopiert. Funktionen, DOM-Knoten und Klasseninstanzen samt Prototyp lassen sich nicht übertragen. Ein Fehler beim Serialisieren wirft auf der Senderseite eine DataCloneError-Exception. Scheitert das Deserialisieren beim Empfänger, wird messageerror ausgelöst.
  • Bei file://-Seiten und Sandbox-iframes ohne allow-same-origin ist event.origin der String "null".
  • Das Ereignis wird asynchron nach dem aktuellen Task zugestellt.

Kompatibilität: wird von allen modernen Browsern unterstützt (Baseline). Workers, MessagePort und BroadcastChannel haben eigene message-Ereignisse, die genauso funktionieren.