CodingNic

Events

Custom Events

Events 15 min read

Custom Events

Objectives

By the end of this chapter, you should be able to:

  • Create your own event with CustomEvent
  • Attach data to a custom event with detail
  • Dispatch a custom event with .dispatchEvent(), and listen for it like any built-in event

💡 Why this matters: Built-in events (click, submit, keydown) cover what a user does. Custom events let different pieces of your own code talk to each other the same way, useful once a page gets bigger than one script.

Creating and Dispatching a Custom Event

new CustomEvent(name, options) creates an event with any name you choose. .dispatchEvent() fires it on an element, exactly like a real browser event.

Given this HTML:

text
<div id="cart"></div>
javascript
const cart = document.getElementById("cart");

cart.addEventListener("item-added", (event) => {
  console.log("Item added event received");
});

const event = new CustomEvent("item-added");
cart.dispatchEvent(event);
// Item added event received

The listener doesn’t know or care that "item-added" isn’t a built-in event name, .addEventListener() works identically for custom and built-in events.

Passing Data with detail

A custom event is far more useful with data attached. The detail option holds whatever value you want, an object is common.

javascript
cart.addEventListener("item-added", (event) => {
  console.log(`Added ${event.detail.name} ($${event.detail.price})`);
});

const event = new CustomEvent("item-added", {
  detail: { name: "Eggs", price: 5 },
});
cart.dispatchEvent(event);
// Added Eggs ($5)

event.detail carries the object through to the listener, unchanged. This is how one part of your code (something that just added an item to a cart) can hand information to another part (something that updates a total, or shows a confirmation message) without those two parts needing to call each other’s functions directly.

Custom Events Bubble Too, If You Let Them

By default, a custom event does not bubble. Pass bubbles: true if you want it to, the same as any other event.

javascript
const event = new CustomEvent("item-added", {
  detail: { name: "Eggs", price: 5 },
  bubbles: true,
});
cart.dispatchEvent(event);

With bubbles: true, a listener attached to cart’s parent, or even document, would also catch this event. Without it, only a listener attached directly to cart receives it.

Try It

Starter HTML for all three exercises:

text
<div id="app">
  <div id="child"></div>
</div>
  1. Attach a listener for a custom event named "ready" to #app, dispatch it, and confirm the listener logs a message.
  2. Attach a listener for a "score-changed" event to #app, dispatch it with detail: { score: 10 }, and log event.detail.score from the listener.
  3. Attach a listener for "item-removed" to #app, dispatch it from #child with bubbles: true, and confirm #app’s listener still fires.

Recap

  • new CustomEvent(name, options) creates an event with any name. .dispatchEvent() fires it, and .addEventListener() listens for it exactly like a built-in event.
  • detail attaches any data you want to the event, read on the listener side as event.detail.
  • Custom events don’t bubble by default. Pass bubbles: true if a listener on an ancestor also needs to catch it.

Next lesson: this module’s exercises, then the mini project, a working to-do list built entirely from the DOM and events.