Skip to content

JavaScript Tutorial Events

JavaScript Custom Events

Lesson 120 of 258 2 min read By Deepak Paswan Updated Oct 4, 2026

You can create your own events and dispatch them on any element. Other parts of your code listen for them with addEventListener, just like click events. This lets components talk to each other without knowing about each other.

index.html
HTML
<div id="cart"></div>
<button id="add">Add to cart</button>
<script>
  const cart = document.querySelector("#cart");

  // a listener, somewhere in the code
  document.addEventListener("cart:add", event => {
    console.log("Item added:", event.detail.name);
    cart.textContent = "Last added: " + event.detail.name;
  });

  // somewhere else: announce what happened
  document.querySelector("#add").addEventListener("click", () => {
    document.dispatchEvent(new CustomEvent("cart:add", { detail: { name: "Notebook", price: 4 } }));
  });
</script>

The parts

  • new CustomEvent(name, options) creates the event.
  • The detail option carries any data you want. It appears as event.detail.
  • element.dispatchEvent(event) fires it.

Bubbling

Custom events do not bubble by default. Add bubbles: true if parents should hear them.

index.html
HTML
<section id="parent">
  <button id="child">Select</button>
</section>
<script>
  document.querySelector("#parent").addEventListener("selected", event => {
    console.log("parent heard:", event.detail);
  });

  document.querySelector("#child").addEventListener("click", event => {
    event.currentTarget.dispatchEvent(new CustomEvent("selected", { detail: "item 7", bubbles: true }));
  });
</script>

Why use them

  • Decoupling. A “cart” can announce cart:add and anything can react, such as a badge, a mini cart, analytics, without the cart code knowing about them.
  • Reusable components. A tabs component can fire tab:change.
  • Matches how built-in events work, so other developers find it familiar.

Naming

Use clear names, often with a prefix to avoid clashes: cart:add, modal:open.

Cancelable events

If you create the event with cancelable: true, listeners can call preventDefault(), and dispatchEvent returns false if they did.

script.js
JS
const ok = document.dispatchEvent(new CustomEvent("before-save", { cancelable: true }));
console.log(ok); // → true   (nobody cancelled it)

The simple version: an EventTarget

You do not need a DOM element. Any EventTarget works:

script.js
JS
const bus = new EventTarget();
bus.addEventListener("ping", event => console.log("got", event.detail));
bus.dispatchEvent(new CustomEvent("ping", { detail: 42 }));
info
Do not overdo it
Events are great for loose connections. If one function simply needs to call another, call it directly. Too many events make a program hard to follow.