⚡IntermediateBrowser JavaScript

Events & Event Handling

Respond to user interactions and browser actions using addEventListener, event objects, delegation, and custom events.

Overview

Events are the mechanism browsers use to communicate that something happened a user clicked a button, typed in an input, scrolled the page, or a network request completed. JavaScript listens for these events and runs code in response, making web pages interactive. The standard way to handle events is addEventListener(type, handler). The handler receives an Event object with details about what happened: which element was clicked, what key was pressed, the mouse position, and more. You can attach multiple listeners to the same element, and remove them later with removeEventListener. Event delegation is a powerful pattern where you attach a single listener to a parent element instead of many listeners to individual children. When a child is clicked, the event bubbles up to the parent, which can check event.target to see what was actually clicked. This works great for dynamic lists and reduces memory usage.

Key Points

  • •addEventListener(type, handler) is the standard way to attach event listeners
  • •The Event object passed to handlers contains target, type, preventDefault(), stopPropagation()
  • •Events bubble up the DOM tree by default a click on a child also fires on all ancestors
  • •event.preventDefault() stops the browser's default action (form submit, link navigation)
  • •event.stopPropagation() prevents the event from bubbling further up the tree
  • •Event delegation: attach listener on parent, use event.target to identify the child that triggered it
  • •removeEventListener requires the exact same function reference used with addEventListener
  • •Custom events: new CustomEvent("name", { detail: data }) lets you dispatch your own events

Examples

Basic event listeners and the Event object

// Assume: <button id="myBtn">Click me</button>
const btn = document.querySelector("#myBtn");

btn.addEventListener("click", function(event) {
  console.log("Clicked!");
  console.log("Target:", event.target);
  console.log("Type:", event.type);     // "click"
  console.log("Button:", event.button); // 0 = left click
});

// Keyboard events
document.addEventListener("keydown", function(event) {
  console.log("Key pressed:", event.key);
  console.log("With Ctrl:", event.ctrlKey);

  if (event.key === "Escape") {
    console.log("Escape pressed closing modal");
  }
});

// Remove a listener (must be same function reference)
function handleClick(e) { console.log("click"); }
btn.addEventListener("click", handleClick);
btn.removeEventListener("click", handleClick); // removes it

Preventing default behavior and event delegation

// Prevent form submission and handle it in JS
const form = document.querySelector("form");
if (form) {
  form.addEventListener("submit", function(event) {
    event.preventDefault(); // stop the page reload
    const data = new FormData(form);
    console.log("Name:", data.get("name"));
    // Handle form data with fetch instead
  });
}

// Event delegation one listener for a dynamic list
const list = document.querySelector("ul");
if (list) {
  list.addEventListener("click", function(event) {
    const li = event.target.closest("li");
    if (!li) return; // clicked outside a list item

    li.classList.toggle("done");
    console.log("Toggled:", li.textContent);
  });

  // New items added later also work automatically
  const newItem = document.createElement("li");
  newItem.textContent = "Dynamic item";
  list.appendChild(newItem);
}

event.target.closest("li") walks up from the click target to find the nearest li ancestor helpful when list items contain child elements.

Custom events and event communication

// Dispatch a custom event
const cart = document.querySelector("#cart") || document.createElement("div");

cart.addEventListener("item-added", function(event) {
  console.log("Item added:", event.detail.name, "Price:", event.detail.price);
  updateCartUI(event.detail);
});

function addToCart(product) {
  // Business logic...
  const event = new CustomEvent("item-added", {
    detail: { name: product.name, price: product.price },
    bubbles: true,   // event bubbles up the DOM
    cancelable: true
  });
  cart.dispatchEvent(event);
}

addToCart({ name: "JavaScript Book", price: 39.99 });

function updateCartUI(item) {
  console.log("UI updated for:", item.name);
}

Common Mistakes

  • Using onclick = handler instead of addEventListener it only allows one listener and gets overwritten
  • Calling removeEventListener with an anonymous function anonymous functions can never be removed
  • Forgetting event.preventDefault() on form submit handlers page reloads and state is lost
  • Not using event.target.closest() when delegating clicking a child element inside a list item gives the wrong target

Note

For event delegation on items that may contain nested HTML (like a button with an icon inside), use event.target.closest(".item") rather than checking event.target directly. closest() walks up from the actual clicked element to find the nearest matching ancestor.

Exercises

Write JavaScript code to solve each exercise. Run it and check your output.

Loading editor…

Completed: 0 / 5 exercises

Knowledge Check

Question 1 of 4

What is event bubbling?