Signatur
Beschreibung
Das Ereignis message wird auf einem MessagePort ausgelöst, wenn der Gegenport (das andere Ende eines MessageChannel) mit postMessage() eine Nachricht sendet. Der Handler bekommt ein MessageEvent. Die Nutzdaten stehen in event.data, übertragene Ports in event.ports.
Einsatzgebiete sind vor allem direkte, private Kommunikationskanäle, etwa zwischen Hauptseite und iframe, zwischen Hauptthread und Worker, bei SharedWorker-Verbindungen oder zwischen zwei Workern. Anders als window.postMessage() ist ein Port ein 1:1-Kanal. Wer den Port besitzt, kann Nachrichten senden und empfangen.
Einen Handler registrierst du auf zwei Arten: mit port.addEventListener("message", handler) oder über die Eigenschaft port.onmessage = handler. Das ist ein wichtiger Unterschied: Das Setzen von onmessage ruft intern port.start() auf. Bei addEventListener musst du port.start() selbst aufrufen. Bis dahin werden eingehende Nachrichten nur in einer Warteschlange gesammelt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Der Ereignisname "message". |
|
| $handler Pflicht | (event: MessageEvent) => void | Callback, der ein MessageEvent erhält. Wichtige Eigenschaften: data (Nutzdaten, per structured clone kopiert), ports (übertragene MessagePort-Objekte), origin und lastEventId (bei Ports meist leere Zeichenkette). |
|
| $options | boolean|AddEventListenerOptions | false | Optionen wie once, signal (AbortSignal zum Entfernen) oder capture. |
Rückgabewert
addEventListener() gibt nichts zurück. Der Handler bekommt ein MessageEvent.Beispiele
Einfacher Kanal mit addEventListener und start()
const { port1, port2 } = new MessageChannel();
port2.addEventListener('message', (event) => {
console.log('Empfangen:', event.data);
});
// Bei addEventListener Pflicht, sonst bleiben die Nachrichten in der Warteschlange
port2.start();
port1.postMessage({ typ: 'gruss', text: 'Hallo Port!' });
onmessage-Eigenschaft: Kommunikation mit einem iframe
// Hauptseite
const iframe = document.querySelector('iframe');
const channel = new MessageChannel();
// onmessage ruft port.start() automatisch auf
channel.port1.onmessage = (event) => {
console.log('Antwort vom iframe:', event.data);
};
iframe.addEventListener('load', () => {
// port2 an das iframe übertragen (Transfer-Liste!)
iframe.contentWindow.postMessage('init', 'https://widget.example.com', [channel.port2]);
});
// Im iframe:
// window.addEventListener('message', (e) => {
// if (e.origin !== 'https://meine-seite.example') return;
// const port = e.ports[0];
// port.onmessage = (ev) => port.postMessage(`Echo: ${ev.data}`);
// port.postMessage('bereit');
// });
Request/Response-Muster mit Promise und Listener entfernen
const { port1, port2 } = new MessageChannel();
// "Server"-Seite: verdoppelt Zahlen
port2.onmessage = ({ data }) => {
port2.postMessage({ id: data.id, ergebnis: data.wert * 2 });
};
const anfrage = (wert) => new Promise((resolve) => {
const id = crypto.randomUUID();
const handler = (event) => {
if (event.data.id !== id) return;
port1.removeEventListener('message', handler);
resolve(event.data.ergebnis);
};
port1.addEventListener('message', handler);
port1.start();
port1.postMessage({ id, wert });
});
const ergebnis = await anfrage(21);
console.log(ergebnis);
SharedWorker: message auf dem Verbindungsport
// shared-worker.js
const verbindungen = [];
self.addEventListener('connect', (event) => {
const port = event.ports[0];
verbindungen.push(port);
port.onmessage = (e) => {
// Nachricht an alle verbundenen Tabs verteilen
verbindungen.forEach((p) => p.postMessage(e.data));
};
});
// main.js
const worker = new SharedWorker('shared-worker.js');
worker.port.onmessage = (e) => console.log('Broadcast:', e.data);
worker.port.postMessage('Hallo aus Tab A');
// Wichtig · Fallstricke
- start() nicht vergessen: Bei
addEventListenerwerden Nachrichten erst zugestellt, wennport.start()aufgerufen wurde.onmessageerledigt das automatisch. Dieser Fehler ist bei Ports sehr häufig. - Kopie statt Referenz:
event.datawird per structured clone kopiert. Funktionen, DOM-Knoten und Klasseninstanzen samt Prototyp gehen dabei nicht mit. Sind die Daten nicht klonbar, wirft schonpostMessage()einenDataCloneError. - Deserialisierungsfehler: Kann eine Nachricht beim Empfänger nicht deserialisiert werden, wird statt
messagedas Ereignismessageerrorausgelöst. Dafür solltest du einen eigenen Handler registrieren. - Sicherheit:
event.originist beiMessagePortin der Regel leer. Die Vertrauensprüfung gehört daher an die Stelle, an der der Port übergeben wird, also in denwindow-message-Handler mite.origin-Prüfung. Eingehende Daten solltest du nie ungeprüft ininnerHTMLoderevalverwenden (XSS-Gefahr). - Übertragen statt kopieren: Ports werden über die Transfer-Liste von
postMessage()übertragen. Danach sind sie im Ursprungskontext nicht mehr nutzbar. - Aufräumen: Mit
port.close()schließt du den Kanal. Solange einonmessage-Handler gesetzt ist, kann ein Port Speicherbereinigung verhindern. - Kompatibilität: Das Ereignis wird von allen modernen Browsern sowie in Workern unterstützt. Node.js bietet ein ähnliches
MessagePortim Modulworker_threads. Dort starten sowohlon('message')als auchaddEventListenerden Port.