Signatur
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
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);
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');
}
};
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) + '…');
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);
// Wichtig · Fallstricke
Sicherheit (wichtig):
- Prüfe immer
event.origingegen 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')oderstartsWith. Diese lassen sich mithttps://example.com.attacker.netaustricksen. Vergleiche exakt mit===. - Behandle
event.dataals nicht vertrauenswürdige Eingabe. Füge die Daten niemals ungeprüft ininnerHTML,eval(),new Function()oderlocation.hrefein (Gefahr von XSS undjavascript:-URLs). Prüfe Typ und Struktur der Daten. - Gib beim Senden sensibler Daten immer eine konkrete
targetOriginan und nicht'*'. Sonst kann ein umgeleitetes Fenster die Daten mitlesen. - Gleiche zusätzlich
event.sourcemit 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-ancestorsundframe-srclegen 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 eigenentype-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, wirdmessageerrorausgelöst. - Bei
file://-Seiten und Sandbox-iframes ohneallow-same-originistevent.originder 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.