Free tools Windows power users keep installed
One-click scans. No signup required.
iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
A JavaScript custom event is an event your own code creates and dispatches on a DOM element to announce something specific to your application, such as an item being added to a cart. You build it with new CustomEvent(), attach any data through its detail property, and listen for it with addEventListener(). Use one when a component should report that something happened and any number of other parts of the page may react, without the component needing to know who they are.
What a custom event is
A DOM event is a signal dispatched to an event target, usually an element. The browser fires many of them on its own, such as click when a user presses a button. A custom event is one your code invents. The name is yours, the timing is yours, and the payload is yours. MDN Web Docs describes these as application-created events, which it distinguishes from events fired by the browser in response to user activity or page changes.
Custom events are a browser DOM pattern, not special JavaScript language syntax. Nothing in the language itself changes. The CustomEvent interface is simply the standard way to create an event that carries extra data.
Create, dispatch, and listen
The whole lifecycle involves four steps. Each one needs the same event type string, and the type is case-sensitive, so "cart-add" and "Cart-Add" are different events.
#1 Best Overall
- Choose a target and a type. The target is an
EventTarget, most often a DOM element. The type is a string such as"cart-add"that names what happened. - Register a listener with
target.addEventListener(type, handler). You can attach several handlers to the same type on the same target. - Construct the event with
new CustomEvent(type, { detail: payload }). If you omitdetail,event.detailisnull. - Dispatch it with
target.dispatchEvent(event). Listeners on that target run during the normal event-processing sequence.
A complete example looks like this:
const card = document.querySelector(".card");
card.addEventListener("cart-add", (event) => {
console.log(event.detail.productId);
});
card.dispatchEvent(new CustomEvent("cart-add", {
detail: { productId: "sku-123" },
}));
Running this logs sku-123 to the console. The detail object is how the emitter passes information to listeners. Keep it small, typically an identifier or a short set of values, so listeners can read what they need without depending on the emitter’s internals.
Remove listeners you no longer need
If a component is torn down or a listener is only needed for a while, remove it with removeEventListener(), passing the same type, the same function reference, and matching options. Anonymous arrow functions cannot be removed because you have no reference to them, so store named handlers when cleanup matters.
Rank #2
function handleCartAdd(event) {
console.log(event.detail.productId);
}
card.addEventListener("cart-add", handleCartAdd);
// later, when the component is destroyed:
card.removeEventListener("cart-add", handleCartAdd);
Set bubbling on purpose
Custom events do not bubble unless you ask them to. The bubbles option defaults to false, so an event dispatched on a card will not reach a listener on its parent list by default. When you want ancestors to handle it, for example to delegate handling to one listener for a whole product grid, set the option explicitly:
PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteconst list = document.querySelector(".product-list");
list.addEventListener("cart-add", (event) => {
console.log("Added", event.detail.productId, "from", event.target);
});
card.dispatchEvent(new CustomEvent("cart-add", {
bubbles: true,
detail: { productId: "sku-123" },
}));
Here card must sit inside .product-list for the listener to fire. Capture-phase listeners also run during dispatch, following the same rules as built-in events. Decide the propagation behavior when you create the event, and treat it as part of the event’s contract with the code that listens to it.
When to use a custom event
Use a custom event when a component or module has a meaningful occurrence to announce and more than one part of the program may care about it, while the emitter should not hold direct references to those parts. The event name states what happened, and detail carries the small payload listeners need. Common cases include:
- A cart widget announcing that an item was added, so a header counter, a toast message, and an analytics module can each respond.
- A dialog announcing that it closed, so the page can restore focus or resume a background task.
- A custom selector announcing the value the user chose, so any form or summary panel can update.
These are illustrative patterns rather than tested recommendations. The benefit in each is that the component stays unaware of its listeners. Adding a new listener later requires no change to the component.
Rank #4
When a direct function call is the better choice
Custom events add indirection, and indirection has a cost. If one known piece of code asks another specific piece to do something, or needs an answer back, a plain function call is usually clearer. Events are one-way notifications. They do not return a value to the dispatcher, and the code that dispatches one has no built-in way to know whether anything handled it.
Recommended Free Tools
A useful rule: use an event to announce that something happened; use a direct call when one part of the code is asking another specific part to act or return a result.
Best Value
| Choice | Use when | Main tradeoff |
|---|---|---|
| Direct function call | One known caller needs another known piece of code to act, or needs a return value | The caller holds a direct reference to the callee, so the two are coupled |
| Custom DOM event | An element should notify one or more unknown listeners that an occurrence took place | Event names, target selection, listener cleanup, and propagation all need to be understood by every party that listens |
The custom event trade-off is decoupling in exchange for less visible control flow. In a small page with one listener, a function call is often easier to follow. The event pattern pays off as the number of interested parts grows.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Event or CustomEvent?
Both interfaces produce events that you dispatch with dispatchEvent(). The difference is the payload. Use Event when the notification carries no data beyond its type. Use CustomEvent when listeners need application-defined data, which it exposes through detail.
| Interface | Carries application data? | Typical use |
|---|---|---|
Event |
No built-in payload field | A signal that something happened, with no details to pass along |
CustomEvent |
Yes, through detail, which is null when omitted |
A notification where listeners need an identifier, value, or small object |
Compatibility and limits
MDN marks CustomEvent as widely available and states that it has been available across browsers since July 2015. Treat that as a broad summary, not a guarantee for every embedded, legacy, or extension environment. Check the compatibility data on the MDN reference page for the specific browsers you support.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problems- Firefox web extensions: MDN notes a caveat when a content script communicates with page scripts and the
detailvalue is not a string. The suggested workaround is to clone the object so the cross-context transfer does not raise a permission error. - Synthetic events: an event your script dispatches is created by application code. It represents the occurrence you chose to announce, not a real user gesture, so do not rely on it to imitate user input in security-sensitive logic.
- Listener order and propagation: listeners run in the order dispatch processes them, and propagation depends on the
bubblessetting and the listener’s phase. Decide these during design, since changing them later can silently break delegated listeners.
For registration, MDN’s addEventListener() reference states: “The addEventListener() method is the recommended way to register an event listener.” That method also supports multiple handlers and phase selection, which is why it is preferred over assigning a single on... property when more than one part of the page needs the same event.
Once you have one custom event working, the pattern extends naturally. Name events for what happened, keep payloads small, and set bubbling explicitly. The simplest version of the code above is usually enough for a first component.
Quick Recap
The Bottom Line
“”
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.

