A page does nothing until somebody touches it. A click, a keystroke, a scroll: each of these gestures produces an event, sent by the browser to the element concerned.
What is still missing is a statement, made in advance, of what should happen next. That is the job of addEventListener.
Definition
addEventListener registers a function, called a listener, to run every time a given type of event reaches the element. It takes three arguments: the event name, the function, and an optional options object.
const button = document.querySelector("#confirm");
button.addEventListener("click", (event) => {
console.log("click on", event.currentTarget.id);
console.log("mouse button:", event.button);
});The object handed over describes the event: the element aimed at, the moment, the keys held down. Its shape changes with the type, a KeyboardEvent carrying a key property that a MouseEvent does not have.
The options
| Option | What it changes |
|---|---|
once | The listener runs a single time, then removes itself |
capture | The listener fires on the way down, not on the way back up |
passive | A promise never to cancel the default action, which smooths scrolling |
signal | An AbortController signal that removes the listener once triggered |
document.addEventListener("keydown", closeModal, { once: true });
zone.addEventListener("touchstart", followFinger, { passive: true });Removing a listener
removeEventListener demands exactly the same arguments as the registration: same type, same function, same capture value. An anonymous function is built afresh on every evaluation, so it can never be found again.
const onClick = () => console.log("click");
button.addEventListener("click", onClick);
button.removeEventListener("click", onClick); // removed
button.removeEventListener("click", () => console.log("click")); // no effectWhen several listeners have to disappear together, a shared AbortController cuts them all in one call.
Registering the same function twice, for the same type and the same options, only adds it once: the duplicate is ignored. Two identical anonymous functions, however, count as two.
Why not onclick
The onclick property still works, but it holds a single function: the second assignment silently overwrites the first. Two pieces of code aiming at the same button then cancel out.
- Several listeners live side by side without disturbing one another, in the order they were added.
- Options such as
onceorcapturedo not exist with the property.
Frequently asked questions
Why does my removeEventListener do nothing?
Because the function handed to the removal is not the one that was registered. Two functions with identical bodies are still two distinct objects. Store the listener in a const before adding it, or go through an abort signal.
Do listeners have to be removed to free memory?
On an element taken out of the document and no longer reachable, the cleanup happens on its own. The real risk concerns listeners placed on document or window from a temporary component: those outlive the component and pile up on every opening.
How do you handle hundreds of clickable elements?
By listening to a single one. Event delegation places the listener on a shared ancestor and identifies the real element at click time, which also covers content added later. That reflex and the traps around it are worked through step by step in the JavaScript course.