CustomEvent – Bausteine, die sich gegenseitig Bescheid sagen, ohne sich zu kennen
Sobald zwei Teile einer Seite aufeinander reagieren sollen, landet man schnell bei globalen Variablen oder Funktionsaufrufen quer durchs Projekt. Eigene Events entkoppeln das – mit denselben Mitteln, die der Browser selbst benutzt.
Der Warenkorb ändert sich, und drei Stellen müssen es wissen: die Zahl im Kopfbereich, der Rabatt-Hinweis und die Schaltfläche „Zur Kasse". Ruft man die drei direkt auf, kennt der Warenkorb plötzlich das ganze Layout – und jede neue Anzeige bedeutet eine Änderung an ihm.
Mit einem eigenen Event dreht sich die Richtung um: Der Warenkorb ruft, und wer will, hört zu.
Auslösen und zuhören
// Auslösen – irgendwo im Warenkorb
document.dispatchEvent(new CustomEvent('warenkorb:geaendert', {
detail: { anzahl: 3, summe: 59.9 }
}));
// Zuhören – irgendwo ganz anders
document.addEventListener('warenkorb:geaendert', e => {
badge.textContent = e.detail.anzahl;
});Die Nutzdaten stehen immer unter detail – das ist der einzige Ort, den CustomEvent dafür vorsieht. Ein Namensraum mit Doppelpunkt (warenkorb:) macht später sofort klar, woher ein Ereignis stammt.
Aus einer Komponente heraus, mit Blasen
class Sternebewertung extends HTMLElement {
waehle(n) {
this.wert = n;
this.dispatchEvent(new CustomEvent('bewertung', {
detail: { wert: n },
bubbles: true, // steigt im DOM nach oben
composed: true, // verlässt auch einen Shadow-DOM
}));
}
}
formular.addEventListener('bewertung', e => {
versteckterInput.value = e.detail.wert; // ein Listener für alle Sterne im Formular
});Ohne bubbles: true bleibt das Ereignis am auslösenden Element – dann muss jeder Zuhörer genau dort registriert sein. Für Komponenten ist Blasen fast immer das, was du willst.
Absagbare Ereignisse: der Zuhörer darf Nein sagen
const ev = new CustomEvent('dialog:schliessen', { cancelable: true });
if (dialog.dispatchEvent(ev)) {
dialog.close(); // niemand hat widersprochen
}
// Anderswo: ungespeicherte Änderungen schützen
dialog.addEventListener('dialog:schliessen', e => {
if (formularGeaendert) e.preventDefault();
});dispatchEvent liefert false, sobald ein Zuhörer preventDefault() aufgerufen hat – dasselbe Muster, das der Browser bei submit benutzt.
Aufräumen nicht vergessen
const ctrl = new AbortController();
document.addEventListener('warenkorb:geaendert', aktualisiere, { signal: ctrl.signal });
// Beim Entfernen der Ansicht: ein Aufruf entfernt alle Listener dieses Controllers
ctrl.abort();Fallstrick
Eigene Events sind synchron: dispatchEvent kehrt erst zurück, wenn alle Zuhörer durchgelaufen sind. Wer darin eine teure Neuberechnung anstößt, blockiert den auslösenden Code. Und lös keine Events in einer Schleife über 500 Einträge aus – sammle die Änderungen und schick ein Ereignis mit der Liste. Zuletzt: Ereignisnamen sind Vertrag. Notiere sie an einer Stelle, sonst hört irgendwann jemand auf warenkorb:aendern statt warenkorb:geaendert und niemand findet den Fehler.
Frontend-Architektur, die auch nach dem dritten Feature noch trägt, ist Handarbeit – ich helfe gern: bymw.de.
Quellen
Du brauchst mehr als ein Snippet?
Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.
Projekt anfragen →Verwandte Snippets
Web Worker – rechnen, ohne die Oberfläche einzufrieren
JavaScript hat im Browser genau einen Faden für alles: Klicks, Animationen, Layout – und deine Rechenschleife. Dauert die zwei Sekunden, ist die Seite zwei Sekunden lang tot. Ein Worker nimmt die Arbeit auf einen eigenen Faden.
Formulare ohne Neuladen abschicken – FormData und fetch
Ein Formular per JavaScript abzuschicken heißt nicht, jedes Feld einzeln auszulesen. FormData nimmt das ganze Formular, fetch schickt es weg – inklusive Dateien, und mit einer Fehlermeldung, die der Besucher versteht.
Promise.allSettled – wenn ein einziger Fehlschlag nicht alles kippen darf
Promise.all bricht beim ersten Fehler ab und wirft alle anderen Ergebnisse weg. Für ein Dashboard aus fünf Quellen ist das genau falsch: Vier Kacheln könnten Daten zeigen, aber die Seite bleibt leer.