JavaScript Basics: Manage Local Listeners with EventTarget
A local component can notify listeners through EventTarget without using a browser DOM. Node supports one-shot listeners, abortable registrations, and cancelable events. The fixture verifies those lifecycle behaviors using synchronous dispatch.
Prepare a scratch directory
Tested on Linux with Node.js v24.19.0. You need the named runtime, Bash, a text editor, and the standard mktemp, rm, and rmdir utilities. The .mjs extension selects ES modules. Assertions check the demonstrated behavior; no npm packages or remote services are needed.
Keep using the same terminal. Stop if setup fails, before saving the source:
start_dir="$PWD"
work_dir="$(mktemp -d)" || exit 1
cd -- "$work_dir" || exit 1Run the example
Save this as demonstrate-event-target.mjs:
import assert from 'node:assert/strict';
const target = new EventTarget();
const controller = new AbortController();
const seen = [];
target.addEventListener('update', event => {
seen.push(`once:${event.detail}`);
}, { once: true });
target.addEventListener('update', event => {
seen.push(`active:${event.detail}`);
}, { signal: controller.signal });
target.dispatchEvent(new CustomEvent('update', { detail: 1 }));
controller.abort();
target.dispatchEvent(new CustomEvent('update', { detail: 2 }));
assert.deepEqual(seen, ['once:1', 'active:1']);
console.log('Observed:', seen.join(', '));
target.addEventListener('save', event => event.preventDefault());
const request = new Event('save', { cancelable: true });
assert.equal(target.dispatchEvent(request), false);
assert.equal(request.defaultPrevented, true);
console.log('Cancelable dispatch accepted:', !request.defaultPrevented);
const notification = new Event('save');
assert.equal(target.dispatchEvent(notification), true);
assert.equal(notification.defaultPrevented, false);
console.log('Noncancelable notification prevented:', notification.defaultPrevented);Run it:
node demonstrate-event-target.mjsObserved output:
Observed: once:1, active:1
Cancelable dispatch accepted: false
Noncancelable notification prevented: falseRead the result
The first update reaches both listeners in registration order. once removes its listener after that dispatch, and aborting the controller removes the other registration before the second dispatch. preventDefault marks a cancelable event and makes dispatchEvent return false. A noncancelable event ignores that cancellation request.
Apply it carefully
Node’s EventTarget is not a DOM tree: do not assume browser bubbling through parents. Dispatch is synchronous for invoking listeners, but it does not await asynchronous listener work. Do not use a listener’s return value as a response channel. In Node, listener exceptions and rejected listener promises need careful handling because they can become uncaught errors; this fixture keeps its listeners synchronous and nonthrowing.
The official reference describes the API and its options. The checks above cover the specific fixture, not every possible input or environment.
Clean up
Remove the source and then the empty practice directory:
rm -- demonstrate-event-target.mjs
cd -- "$start_dir" || exit 1
rmdir -- "$work_dir"
unset start_dir work_dirIf the directory is not empty, inspect the remaining files rather than deleting unknown contents.
Acknowledgements
This post was created with assistance from AI.