The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEScan for outdated or missing drivers - takes under a minuteDriver Scan →A JavaScript custom event is an event your own code creates and sends to a DOM element so that any listeners attached to it can react. You make one with new CustomEvent(), attach its data through the detail property, and send it with dispatchEvent(). Use one when a component needs to announce that something happened and several parts of the page may care, without knowing in advance which parts those are.
What a custom event actually is
The browser fires built-in events such as click, input, and submit in response to user actions and page activity. A custom event uses the same machinery, but your application defines its name and decides when it fires. The name can be anything that describes the occurrence, such as cart-add or dialog-closed. Note that this is a browser DOM feature, not special syntax in the JavaScript language itself.
As an Amazon Associate I earn from qualifying purchases.
The CustomEvent interface, documented by MDN Web Docs, extends the basic Event interface with one extra property, detail. That property carries whatever data listeners need to know about the occurrence. If you do not need to pass data, a plain Event is enough.
Recommended Free Tools
Creating and dispatching a CustomEvent
The whole pattern takes four moves. Here is the sequence, with the code for each step:
#1 Best Overall
-
Pick a target and a type. The target is the
EventTarget(usually a DOM element) that will send and receive the event. The type is a string such as"cart-add". Type strings are case-sensitive, so"Cart-Add"and"cart-add"are different events and a mismatch fails silently. -
Register a listener. Call
addEventListener(type, handler)on the target. Unlike assigning to anon...property,addEventListenerlets several handlers coexist on the same type, and it accepts options for capture and bubbling phases. -
Construct the event. Pass the type and an options object. If listeners need data, put it in
detail. When you omitdetail, it defaults tonull.Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy. -
Dispatch it. Call
target.dispatchEvent(event). The browser then runs the normal event-processing rules for that target, including capture and, if you asked for it, bubbling.
const card = document.querySelector(".card");
card.addEventListener("cart-add", (event) => {
console.log(event.detail.productId); // "sku-123"
});
card.dispatchEvent(new CustomEvent("cart-add", {
detail: { productId: "sku-123" },
}));
Running this logs sku-123 to the console. The listener runs synchronously during the dispatchEvent() call, so code placed after that call runs only after every listener has finished.
Bubbling: why the default matters
Custom events do not bubble unless you say so. A dispatched event goes to its target and to any capture-phase listeners along the path, but it does not travel up to ancestor elements unless the event was created with bubbles: true. This is the setting you need when you want a single listener on a container to handle events from many children, which is event delegation.
const event = new CustomEvent("cart-add", {
bubbles: true,
detail: { productId: "sku-123" },
});
card.dispatchEvent(event);
Set propagation options on purpose, and write them down in the component’s documentation. A listener that works only because the event happens to bubble will break when someone moves the element or changes its wrapper.
When to use a custom event
A custom event fits when a component or module has a meaningful occurrence to announce, when more than one listener may care, and when the emitter should not hold references to those listeners. Common cases include:
- A product card announcing that an item was added to a cart, so a header counter, a mini-cart, and an analytics module can each respond.
- A dialog announcing that it closed, so the page can restore focus or re-enable scrolling.
- A custom picker or slider announcing a new selected value, so a form or a preview can update.
These cases are illustrations of the pattern, not measured results from any particular project.
Rank #4
When a direct function call is the better choice
Events add indirection, and indirection costs readability. If one specific piece of code asks another specific piece to do something, or needs a return value immediately, call the function directly. Events do not return values from listeners to the dispatcher, so a dispatch is a poor way to ask a question.
| Situation | Better choice | Main tradeoff |
|---|---|---|
| One known caller needs a result right away | Direct function call | Caller and callee are directly connected |
| Something happened and any number of parts may react | Custom event | You must manage event names, target choice, listener cleanup, and propagation |
| Announcement needs no data | new Event(type) |
Listeners cannot read a payload |
| Announcement carries application data | new CustomEvent(type, { detail }) |
Payload is only as clear as the documented shape of detail |
A useful test: if the sentence describing the code begins with “something happened,” use an event. If it begins with “do this” or “tell me this,” use a function call.
Free tools Windows power users keep installed
One-click scans. No signup required.
Removing listeners and avoiding leaks
If the listener’s lifetime is shorter than the element’s, remove it when that lifetime ends. Call removeEventListener() with the same type, the same function reference, and the same capture option you used when adding it. An anonymous arrow function passed to addEventListener cannot be removed later, so store it in a variable first.
Best Value
function onClosed(event) {
console.log(event.detail.reason);
}
dialog.addEventListener("dialog-closed", onClosed);
// later, when the listener should stop:
dialog.removeEventListener("dialog-closed", onClosed);
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Compatibility and limits
MDN describes CustomEvent as widely available and says it has been available across browsers since July 2015. Treat that as a broad summary. Older embedded browsers, legacy environments, and unusual hosts can differ, so check your own target list before relying on it.
MDN also notes one specific Firefox caveat. When a web extension content script tries to send an event with a non-string detail value to a page script, Firefox can raise a permission error. MDN suggests cloning the object before passing it. Keeping detail to plain strings, numbers, and simple objects avoids the question altogether.
A dispatched event is created by your code, not by the browser. A manually dispatched cart-add event does not represent a real click or keypress by a user, so do not treat it as proof that a user performed an action.
Quick Recap
Common mistakes
- Mismatched type strings. Because the type is case-sensitive, a typo produces no error. Log the event type inside the listener while debugging.
- Listening on the wrong target. A listener on
documentwill not see an event dispatched on a detached element that never joins the document tree. - Relying on accidental bubbling. Set
bubblesexplicitly whenever a parent is supposed to hear the event. - Undocumented payloads. Write down the keys and types in
detail. Listeners written later will depend on that shape.
Practical checklist before you ship a custom event
- The name describes something that already happened, in past tense or as a clear state change.
- The target is the element that owns the occurrence.
- Bubbling and cancelability are set on purpose, not left at defaults by accident.
detailis small, documented, and cloneable.- Every listener that can outlive its usefulness has a matching
removeEventListener()call.
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




