JavaScript DOM Events

React to Clicks, Keys, and Everything In Between

JavaScript DOM Events

`addEventListener` wires functions to user actions. Learn the event object, default actions, bubbling, and delegation.

6 min read Level 2/5 #DOM#events#addEventListener
What you'll learn
  • Wire up event listeners with `addEventListener`
  • Use the event object and prevent defaults
  • Use event delegation for dynamic lists

The DOM is event-driven. Clicks, key presses, form submits, scrolls, network responses — all of them are events you can listen for.

The Standard Listener

Wire a click script.js
const btn = document.querySelector("button");

btn.addEventListener("click", () => {
  console.log("clicked!");
});
▶ Preview: console

The first argument is the event name (no "on" prefix); the second is the function to run. You can attach multiple listeners to the same element — they all fire.

The Event Object

The callback gets an event object with details about what happened.

Reading event details script.js
document.addEventListener("click", e => {
  console.log(e.type);     // "click"
  console.log(e.target);   // the element that was clicked
  console.log(e.x, e.y);   // viewport coordinates
});
▶ Preview: console

Common properties:

PropertyWhat it tells you
e.typeThe event name ("click", "keydown", etc.)
e.targetThe element the event happened on
e.currentTargetThe element the listener is attached to
e.keyOn keyboard events: the key ("Enter", "a")
e.preventDefault()Cancel the browser’s default behavior
e.stopPropagation()Stop the event from bubbling further

Preventing Defaults

Some events have built-in browser behavior — submitting a form navigates, clicking a link follows it. preventDefault() cancels that so you can handle it yourself.

Stop a form from navigating script.js
const form = document.querySelector("form");
form.addEventListener("submit", e => {
  e.preventDefault();              // don't actually submit
  console.log("would submit here");
});
▶ Preview: console

Bubbling

Events bubble up the tree: a click on a <span> inside a <button> inside a <div> fires on the span first, then the button, then the div — all the way up to document. Most events bubble.

Delegation — One Listener for Many

Instead of attaching a listener to every list item, attach one to the parent and check e.target.

Delegated click script.js
const list = document.querySelector(".todo");

list.addEventListener("click", e => {
  if (e.target.matches(".todo__delete")) {
    e.target.closest("li").remove();
  }
});
▶ Preview: console

Why it matters: when you add or remove items later, the listener still works — there’s nothing to re-wire. el.matches(selector) asks “does this element match the selector?” and el.closest(selector) walks up until something does.

Removing Listeners

To remove a listener, you need the same function reference.

Remove a listener script.js
function onClick() { console.log("hi"); }

const btn = document.querySelector("button");
btn.addEventListener("click", onClick);
btn.removeEventListener("click", onClick);   // gone
▶ Preview: console

Anonymous arrow functions can’t be removed because you have no handle to them.

once and Other Options

The third argument to addEventListener accepts an options object.

Auto-remove after first fire script.js
document.addEventListener("click", () => console.log("one shot"), {
  once: true,           // remove after firing once
});
▶ Preview: console

Other useful flags: { capture: true } (fire on the way DOWN instead of bubbling up), { passive: true } (promise not to call preventDefault — improves scroll performance).

Up Next

Forms get their own little world of events and APIs.

JavaScript DOM Forms →