0Pricing
JavaScript Academy · Lesson

Event Delegation Pattern

Handle many child events from one parent listener.

Event Delegation Pattern is a free JavaScript Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the JavaScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

One Listener to Rule Them All

Event delegation means attaching a single listener to a common ancestor instead of many listeners on individual children. Because events bubble, the parent receives clicks from all its descendants.

<ul id="menu">
  <li>Home</li>
  <li>About</li>
  <li>Contact</li>
</ul>

The Naive Approach

Without delegation you would loop over every item and attach a handler. That is wasteful: more memory, and it breaks for elements added later.

document.querySelectorAll("#menu li").forEach((li) => {
  li.addEventListener("click", () => console.log(li.textContent));
});

The Delegated Approach

Instead, attach one listener to the parent and inspect event.target to find out which child was clicked.

const menu = document.getElementById("menu");
menu.addEventListener("click", (e) => {
  console.log(e.target.textContent);
});

Matching the Right Target

The click may land on a nested element. Use event.target.closest(selector) to walk up and find the relevant ancestor matching your selector.

menu.addEventListener("click", (e) => {
  const item = e.target.closest("li");
  if (!item) return; // clicked outside any li
  console.log("Clicked:", item.textContent);
});

Guarding With a Check

Always guard against clicks that do not match. The if (!item) return; early exit keeps your handler from crashing when the user clicks padding or a gap inside the container.

menu.addEventListener("click", (e) => {
  const item = e.target.closest("li");
  if (!item || !menu.contains(item)) return;
  handleSelect(item);
});

Using Data Attributes

A common pattern stores an id or action on each item with a data-* attribute. The delegated handler reads it from the matched element.

menu.addEventListener("click", (e) => {
  const item = e.target.closest("li");
  if (!item) return;
  const id = item.dataset.id;   // reads data-id
  console.log("Selected id:", id);
});

Dispatching by Action

For toolbars with many buttons, give each a data-action and switch on it. One listener handles every button.

toolbar.addEventListener("click", (e) => {
  const btn = e.target.closest("button");
  if (!btn) return;
  switch (btn.dataset.action) {
    case "save": save(); break;
    case "delete": remove(); break;
  }
});

Why It Scales

With delegation, a list of 1000 rows needs just one listener instead of 1000. Less memory, faster setup, and no cleanup loop when rows change.

Performance Note

Delegation does have a small cost: every matching event runs your handler, which then filters. For most apps this is negligible compared to the savings, but avoid attaching delegated listeners to document for very high-frequency events like mousemove.

Choosing the Container

Pick the nearest stable ancestor that contains all targets. Delegating to a tight container (the list itself) is better than delegating to document, because fewer unrelated events trigger your handler.

const list = document.querySelector(".task-list");
list.addEventListener("click", onTaskClick);

Combining With closest

closest() is the heart of delegation. It returns the element itself if it matches, or the nearest matching ancestor, or null. This makes nested markup safe to delegate over.

const cell = e.target.closest("td[data-col]");
if (cell) console.log(cell.dataset.col);

Quick Check

Why use event delegation?

Recap

Event delegation attaches one listener to a parent and uses bubbling to handle all children. Use e.target.closest(selector) to find the relevant element, guard with an early return, and read data-* attributes to dispatch actions. It scales to huge lists and is the standard pattern for dynamic UIs.

Frequently asked questions

Is the “Event Delegation Pattern” lesson free?

Yes — the full text of “Event Delegation Pattern” is free to read here on the web, and the JavaScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the JavaScript Academy course, upgrade to CoddyKit PRO.

What will I learn in “Event Delegation Pattern”?

Handle many child events from one parent listener. You practise JavaScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start JavaScript Academy?

No prior experience is required. JavaScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Event Delegation Pattern” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this JavaScript Academy lesson?

Yes. Every JavaScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The Capture and Bubble Phases
  2. Event Delegation Pattern
  3. Dynamic Lists with Delegation
  4. Custom Events
← Back to JavaScript Academy