Крім вбудованих подій (click, submit), можна створювати власні - щоб частини інтерфейсу спілкувалися, не знаючи одна про одну.
// компонент кошика повідомляє, що змінилася кількість товарів
const event = new CustomEvent('cart:updated', {
detail: { count: 3, total: 1250 },
bubbles: true,
});
cartElement.dispatchEvent(event);
// лічильник у шапці слухає, не знаючи про кошик нічого, крім назви події
document.addEventListener('cart:updated', (event) => {
badge.textContent = event.detail.count;
});
Параметри:
detail- довільні дані події;bubbles: true- подія спливає до предків. За замовчуваннямfalse: без цього її почують лише обробники на самому елементі, і слухач наdocumentмовчатиме - найчастіша помилка;cancelable: true- слухачі можуть викликатиpreventDefault(), а той, хто відправив подію, - перевірити результат:
const allowed = form.dispatchEvent(new CustomEvent('order:before-submit', { cancelable: true, bubbles: true }));
if (!allowed) return; // хтось із слухачів скасував
composed: true- подія перетинає межу Shadow DOM (для веб-компонентів).
dispatchEvent синхронний: усі обробники виконуються до того, як виклик поверне керування. Це не черга повідомлень.
Назви подій - з префіксом-простором імен (cart:updated, modal:open), щоб не зіткнутися з вбудованими чи чужими подіями.
Де це використовується у TALL-стеку:
- Alpine:
$dispatch('notify', { message: 'Збережено' })створюєCustomEventзі спливанням, а@notify.window="..."слухає його наwindow; - Livewire:
$this->dispatch('post-created')у PHP доходить до браузера як подія, яку слухають черезLivewire.on(...)чи@post-created.windowв Alpine. Так серверний компонент повідомляє JavaScript-коду про зміни; - інтеграція сторонніх бібліотек (редакторів, карт): бібліотека відправляє подію, Alpine чи Livewire її підхоплюють.
Альтернатива без DOM - EventTarget як шина подій: const bus = new EventTarget(); і ті самі addEventListener/dispatchEvent, коли прив'язка до елемента не потрібна.