JavaScript provides multiple ways to define and trigger custom events, enabling decoupled communication between components. Below are the modern and legacy approaches for implementing custom events.
Using the Event Constructor
The Event constructor allows creation of simple custom events without attached data. It accepts two parameters: the event type as a string, and an optional configuration object.
const customEvent = new Event('dataReady', {
bubbles: true,
cancelable: false,
composed: true
});
document.getElementById('container').addEventListener('dataReady', (e) => {
console.log('Data is ready, but no payload attached.');
});
document.getElementById('container').dispatchEvent(customEvent);
The configuration object supports three optional properties:
- bubbles: If
true, the event propagates up the DOM tree. - cancelable: If
true, callingpreventDefault()on the event has an effect. - composed: If
true, the event can cross in to Shadow DOM boundaries.
Note: This method does not support attaching arbitrary data and is not supported in older versions of Internet Explorer.
Using CustomEvent for Data-Driven Events
CustomEvent extends Event by adding a detail property, allowing structured data to be passed along with the event. This is ideal for scenarios where components need to exchange information.
const eventData = {
userId: 12345,
timestamp: Date.now(),
action: 'login'
};
const userLoginEvent = new CustomEvent('userLogin', {
detail: eventData,
bubbles: true,
cancelable: true
});
window.addEventListener('userLogin', (e) => {
console.log('User login triggered:', e.detail.userId);
console.log('Action:', e.detail.action);
});
window.dispatchEvent(userLoginEvent);
The detail property can hold any JavaScript value — objects, arrays, primitives — making CustomEvent suitable for complex communication patterns, including use within Web Workers.
Legacy: document.createEvent() and initCustomEvent()
This approach is deprecated and should be avoided in new code. It was historically used to create and initialize events before the constructor-based APIs became standard.
window.addEventListener('configUpdate', (e) => {
console.log('Received config update:', e.data);
});
setTimeout(() => {
const event = document.createEvent('CustomEvent');
event.initCustomEvent('configUpdate', true, true, null);
event.data = { theme: 'dark', language: 'en' };
window.dispatchEvent(event);
}, 3000);
Although functional, this method lacks type safety and is inconsistent across browsers. Modern alternatives should always be preferred.