Signatur
Beschreibung
CustomEvent erweitert Event um die schreibgeschützte Eigenschaft detail. Darin lassen sich beliebige Daten ablegen, etwa Objekte, Arrays oder primitive Werte. Das Ereignis wird mit new CustomEvent(type, options) erzeugt und mit EventTarget.dispatchEvent() an einem Element, an window, document oder einem eigenen EventTarget ausgelöst.
Typische Einsatzfälle sind die lose gekoppelte Kommunikation zwischen Komponenten (z. B. Web Components, die "item-selected" nach außen melden), ein einfaches Publish/Subscribe-System auf Basis von EventTarget und das Signalisieren von Zustandsänderungen, ohne dass Aufrufer und Empfänger sich direkt kennen.
Das Ereignis durchläuft das DOM nach den üblichen Regeln. Ohne bubbles: true steigt es nicht im DOM auf, ohne cancelable: true hat preventDefault() keine Wirkung, und ohne composed: true bleibt es innerhalb eines Shadow-DOM. dispatchEvent() arbeitet synchron: Alle Listener laufen durch, bevor der Aufruf zurückkehrt.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $type Pflicht | string | Name des Ereignisses, z. B. "cart:updated". Groß- und Kleinschreibung wird unterschieden. Empfohlen sind Kleinbuchstaben mit Bindestrich oder Doppelpunkt. |
|
| $options | CustomEventInit | {} | Optionales Objekt mit folgenden Feldern:
|
Rückgabewert
CustomEvent-Instanz. Die mitgegebenen Daten liegen schreibgeschützt in event.detail.Beispiele
Ereignis erzeugen, auslösen und detail auslesen
const button = document.createElement('button');
button.addEventListener('user:login', (event) => {
console.log(`Willkommen, ${event.detail.name}!`);
console.log(event instanceof CustomEvent, event.bubbles);
});
const loginEvent = new CustomEvent('user:login', {
detail: { name: 'Anna', id: 42 }
});
button.dispatchEvent(loginEvent);
Web Component meldet eine Auswahl nach außen (bubbles + composed)
class ColorPicker extends HTMLElement {
connectedCallback() {
const shadow = this.attachShadow({ mode: 'open' });
shadow.innerHTML = '<button data-color="red">Rot</button><button data-color="blue">Blau</button>';
shadow.addEventListener('click', (e) => {
const color = e.target.dataset.color;
if (!color) return;
this.dispatchEvent(new CustomEvent('color-change', {
detail: { color },
bubbles: true,
composed: true
}));
});
}
}
customElements.define('color-picker', ColorPicker);
document.body.append(document.createElement('color-picker'));
// Listener weiter oben im DOM – funktioniert dank bubbles/composed
document.addEventListener('color-change', (e) => {
document.body.style.background = e.detail.color;
console.log('Neue Farbe:', e.detail.color);
});
Abbrechbares Ereignis: Aktion per preventDefault() verhindern
const form = document.createElement('form');
form.addEventListener('before-save', (e) => {
if (e.detail.title.trim() === '') {
e.preventDefault(); // Speichern verhindern
}
});
const save = (data) => {
const event = new CustomEvent('before-save', { detail: data, cancelable: true });
const allowed = form.dispatchEvent(event); // false, wenn abgebrochen
console.log(allowed ? 'Gespeichert' : 'Speichern abgebrochen');
};
save({ title: '' });
save({ title: 'Bericht Q3' });
Einfacher Event-Bus mit EventTarget
class EventBus extends EventTarget {
emit(type, detail) {
this.dispatchEvent(new CustomEvent(type, { detail }));
}
on(type, handler) {
const listener = (e) => handler(e.detail);
this.addEventListener(type, listener);
return () => this.removeEventListener(type, listener); // Abmelden
}
}
const bus = new EventBus();
const off = bus.on('cart:updated', ({ count }) => console.log(`Warenkorb: ${count} Artikel`));
bus.emit('cart:updated', { count: 3 });
off();
bus.emit('cart:updated', { count: 4 }); // keine Ausgabe mehr
// Wichtig · Fallstricke
- Standardmäßig kein Bubbling: Ohne
bubbles: trueerreicht das Ereignis nur Listener direkt am Ziel. Delegierte Listener andocumentbekommen es dann nicht mit. Das ist ein sehr häufiger Fehler. - Shadow DOM: Soll ein Ereignis eine Web Component verlassen, wenn es im Shadow-Root ausgelöst wurde, braucht es zusätzlich
composed: true. detailist schreibgeschützt: Die Eigenschaft selbst kann nicht neu zugewiesen werden. Ein übergebenes Objekt bleibt aber veränderbar, und alle Listener teilen sich dieselbe Referenz. Wer Nebenwirkungen vermeiden will, übergibt eine Kopie oder friert das Objekt mitObject.freeze()ein.- Synchron:
dispatchEvent()ruft alle Listener sofort nacheinander auf. Wirft ein Listener eine Ausnahme, wird sie gemeldet, die übrigen Listener laufen aber weiter. - Veraltete API:
document.createEvent('CustomEvent')zusammen mitinitCustomEvent()gilt als veraltet. Heute nutzt man immer den Konstruktor. Der Internet Explorer unterstützte den Konstruktor nicht und brauchte ein Polyfill. isTrustedist bei selbst ausgelösten Ereignissen immerfalse. Sicherheitsrelevante Entscheidungen, etwa ob eine echte Nutzeraktion vorliegt, dürfen sich nicht auf eigene Ereignisse stützen.- Node.js:
CustomEventist seit Node.js 19 global verfügbar, in Node.js 18 nur mit dem Flag--experimental-global-customevent. - Eindeutige Ereignisnamen mit Präfix (z. B.
"myapp:ready") verhindern Konflikte mit nativen oder fremden Ereignissen.