Custom Events & Event Dispatching0%

Custom Events & Event Dispatching

Beginner12 min read•Updated: Jul 11, 2026
Study Materials

Custom Events & Event Dispatching in Modern JavaScript

While standard DOM events (click, input, keydown) handle direct user input, modular architectures require components to communicate domain-specific events—such as cart:item-added, modal:closed, or user:authenticated. The CustomEvent API allows you to create, dispatch, and intercept custom events across the DOM and Web Components.


1. Creating and Dispatching a CustomEvent

A CustomEvent takes an event type string and an optional initialization dictionary:

JavaScript
// Step 1: Instantiate the Custom Event
const itemAddedEvent = new CustomEvent('cart:item-added', {
detail: {
productId: 'SKU-9921',
name: 'Ergonomic Keyboard',
price: 129.99,
timestamp: Date.now()
},
bubbles: true, // Allows event to bubble up through parent DOM ancestors
composed: true, // CRITICAL: Allows event to cross Shadow DOM boundaries!
cancelable: true // Allows listeners to call event.preventDefault()
});
 
// Step 2: Dispatch from any DOM element or window
const cartButton = document.querySelector('#btn-add-to-cart');
cartButton.dispatchEvent(itemAddedEvent);

2. Listening to Custom Events & Accessing detail

Event payloads passed in { detail } are available on event.detail:

JavaScript
// Listener attached to ancestor or window
window.addEventListener('cart:item-added', (event) => {
console.log('Item Added Event Intercepted!');
console.log('Product Name:', event.detail.name);
console.log('Price:', event.detail.price);
 
// Update navigation badge
updateNavCartBadge();
});

3. Crossing the Shadow DOM: composed: true

By default, events dispatched inside a Shadow DOM are trapped inside and cannot bubble up into the main document. Setting composed: true allows the event to penetrate through the shadow boundary into the outer Light DOM:

Output
Inside Shadow DOM:
button.dispatchEvent(new CustomEvent('user-action', {
bubbles: true,
composed: true <── Crosses #shadow-root!
}))
│
▼
Main Document receives event: window.addEventListener('user-action')

4. Making Custom Events Cancelable

If you set cancelable: true, listeners can call event.preventDefault(). The caller of dispatchEvent() receives a boolean indicating whether the action was permitted:

JavaScript
// Component dispatching event:
function requestDeleteFile(fileName) {
const event = new CustomEvent('file:before-delete', {
detail: { fileName },
cancelable: true
});
 
// dispatchEvent returns FALSE if any listener called preventDefault()!
const isAllowed = window.dispatchEvent(event);
 
if (isAllowed) {
console.log(`Proceeding with deletion of: ${fileName}`);
deleteFile(fileName);
} else {
console.warn(`Deletion of ${fileName} was cancelled by an event listener.`);
}
}
 
// Security guard listener canceling deletion:
window.addEventListener('file:before-delete', (e) => {
if (e.detail.fileName.endsWith('.protected')) {
e.preventDefault(); // Cancel deletion!
alert('Cannot delete protected system files!');
}
});

Interactive Quiz

Practice Quiz

Question 1
What property of a CustomEvent holds the arbitrary payload data passed to it?
Question 2
What option must be set to true on a CustomEvent so it can bubble out of a Shadow DOM into the main Light DOM?
Question 3
What does element.dispatchEvent(event) return if a listener calls event.preventDefault() on a cancelable event?
Question 4
Can custom events bubble up through ancestor elements like standard click events?
Question 5
What is the primary architectural benefit of using Custom Events for component communication?
Next Lesson

Debouncing vs Throttling Deep Dive

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext