addEventListener: Options and Clean Teardownelement.addEventListener() attaches one or more event handlers to a DOM element without overwriting earlier listeners. Passing the options object lets you run a handler only once, keep scrolling silky smooth, or remove multiple listeners cleanly with AbortController.
addEventListener OptionsPass an options object as the third argument to fine-tune listener behavior:
{ once: true }: Automatically removes the listener after it runs one time—ideal for first-click onboarding tips or one-shot transitions.{ passive: true }: Promises the browser your handler will never call event.preventDefault(), allowing touch and wheel scrolling to run smoothly without waiting on JavaScript.{ capture: true }: Fires your handler during the downward capturing phase instead of waiting for bubbling.{ signal: controller.signal }: Links the listener to an AbortController so calling controller.abort() removes every linked listener at once.AbortController Cleanup ExampleUsing an AbortController removes the need to store named references to every single arrow callback just to call removeEventListener().
const app = document.getElementById("app");
if (app) {
const btn = document.createElement("button");
btn.textContent = "Run Action";
app.appendChild(btn);
const controller = new AbortController();
btn.addEventListener(
"click",
() => {
console.log("Handled click cleanly via AbortSignal!");
},
{ signal: controller.signal }
);
btn.click(); // Fires listener
controller.abort();
btn.click(); // Ignored because listener was removed!
console.log("Listener removed via AbortController.");
}
removeEventListener(): Passing two separate () => {} functions creates two distinct references, so removeEventListener silently fails to detach the first one.handleSave() with parentheses: btn.addEventListener("click", handleSave()) runs handleSave immediately on setup and passes its return value (undefined) instead of the function itself.preventDefault() inside a passive: true listener: Browsers ignore preventDefault() inside passive listeners and print a console warning.click listeners to the same button?Yes, addEventListener() queues multiple handlers for the same event type and runs them in registration order, unlike btn.onclick = fn, which overwrites prior handlers.
Wrap the call inside a tiny arrow function, like btn.addEventListener("click", (e) => handleDelete(e, item.id)), and pair it with { signal } if you need teardown.
touchstart and wheel listeners trigger passive warnings?Browsers default touchstart and wheel listeners on document and window to passive: true so slow JS handlers cannot freeze mobile scrolling.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.