Handle Events on a List Parent

Handle Events on a List Parent

Events can bubble from a child to its ancestors. Delegation attaches a listener to the class list, finds the relevant button and also supports buttons added later.

Before this topic, review Validate a Student Mark if its terms are unfamiliar.

What you’ll learn

  • Explain handle events on a list parent through the school example.
  • Trace the shown operations and distinguish their result from displayed output.
  • Make the practice change and explain its consequence: The child click still bubbles; the parent handler reads the edited button data.

Work through the school example

This is JavaScript in a browser. Read the initial values, then follow each operation to its displayed result. A declaration names a value, a function call performs an operation, and console.log displays its arguments.

Create this markup in a separate index.html, then place the JavaScript in a script after these elements. Use this isolated practice page so the selectors refer to its markup.

<div id="students"><button data-roll="2"><span>Diya</span></button></div>

JavaScript · browser · complete script for the supplied markup

const list = document.querySelector("#students");
list.addEventListener("click", (event) => {
  const button = event.target.closest("button[data-roll]");
  if (button && list.contains(button)) console.log(button.dataset.roll);
});
list.querySelector("span").click();

Expected output

2

Introduction to DOM Events

In JavaScript, DOM events are actions that occur in the browser, such as a user clicking a button, pressing a key, or moving the mouse. These events can be listened to and handled using addEventListener.

Everything in JavaScript happens inside the browser environment through these events. You write functions that respond to those events — known as event handlers.

Listening to Events with addEventListener

You can attach a listener to any DOM element that listens for a specific event like click, keydown, mouseenter, etc.

// Select an element by ID
const button = document.getElementById('myButton');

// Add click event listener
button.addEventListener('click', function () {
  console.log('Button was clicked!');
});
Button was clicked!

What Happens When You Click?

When the user clicks on the button, the callback function inside addEventListener is triggered and logs the message.

Why Use Event Delegation?

Imagine you have a long list of items, and you want to handle clicks on each of them. Attaching individual listeners to each item would be inefficient.

Event delegation solves this by attaching a single listener to a common parent. The event bubbles up from the child to the parent, and we handle it at the parent level.

Example: Event Delegation with a List

// Parent container
const parent = document.getElementById('listContainer');

// Add one listener to the parent
parent.addEventListener('click', function (event) {
  if (event.target.tagName === 'LI') {
    console.log('Clicked item:', event.target.textContent);
  }
});
Clicked item: Item 3

How Does This Work?

Every time you click on a list item, the event bubbles up to the parent #listContainer. The parent handles the event and checks if the original click was on an LI tag using event.target.

💡 Question:

What would happen if you clicked on whitespace in the parent that is not part of a child LI?

Answer: Nothing would be logged because the condition event.target.tagName === 'LI' would be false.

Real-World Use Case

Event delegation is commonly used in dynamic lists, menus, or UI elements generated at runtime — where attaching listeners to each element is either costly or impossible ahead of time.

Example: Handling Buttons with Delegation

// Wrapper that contains multiple buttons
const wrapper = document.getElementById('buttonWrapper');

// Single event listener for all buttons
wrapper.addEventListener('click', function (e) {
  if (e.target.tagName === 'BUTTON') {
    console.log('You clicked:', e.target.textContent);
  }
});
You clicked: Save

Benefits of Event Delegation

  • Performance: Fewer listeners means less memory usage.
  • Dynamic Support: Works for elements added after page load.
  • Cleaner Code: Keeps logic in one place (the parent).

Tips for Beginners

  • Use event.target to determine what was clicked.
  • Use event.currentTarget to refer to the element the listener is bound to.
  • Always check the tagName or use class checks to avoid unwanted triggers.

Common Mistakes to Avoid

  • Not checking event.target — which can lead to errors when parent or whitespace is clicked.
  • Attaching too many listeners — slows down performance on large DOM trees.

Recognize the important boundary

target is the original event target; currentTarget is the listener's element. Some events do not bubble and delegation needs an appropriate event choice.

Try a small variation

JavaScript · original source for practice

const list = document.querySelector("#students");
list.addEventListener("click", (event) => {
  const button = event.target.closest("button[data-roll]");
  if (button && list.contains(button)) console.log(button.dataset.roll);
});
list.querySelector("span").click();

Your task: Replace data-roll="2" with data-roll="3". Predict the result before running the edited program.

The source is repeated here. If the edit targets the HTML rather than the script, use the markup repeated below; the worked answer also repeats the changed markup.

Markup used by this section:

<div id="students"><button data-roll="2"><span>Diya</span></button></div>

Worked answer and reasoning

The child click still bubbles; the parent handler reads the edited button data.

Markup for this edited program:

<div id="students"><button data-roll="3"><span>Diya</span></button></div>

JavaScript · complete worked variation

const list = document.querySelector("#students");
list.addEventListener("click", (event) => {
  const button = event.target.closest("button[data-roll]");
  if (button && list.contains(button)) console.log(button.dataset.roll);
});
list.querySelector("span").click();

Expected output

3

Predict the original program

JavaScript · original program for this question

const list = document.querySelector("#students");
list.addEventListener("click", (event) => {
  const button = event.target.closest("button[data-roll]");
  if (button && list.contains(button)) console.log(button.dataset.roll);
});
list.querySelector("span").click();

Markup used by this section:

<div id="students"><button data-roll="2"><span>Diya</span></button></div>
In the browser, what does the original JavaScript example display, in order?
Choose one answer. You can try again.

Answer and explanation

2

Events can bubble from a child to its ancestors. Delegation attaches a listener to the class list, finds the relevant button and also supports buttons added later. target is the original event target; currentTarget is the listener's element. Some events do not bubble and delegation needs an appropriate event choice.

Keep the useful idea

Events can bubble from a child to its ancestors. Delegation attaches a listener to the class list, finds the relevant button and also supports buttons added later.

Check your variation against this explanation: The child click still bubbles; the parent handler reads the edited button data.

Use the JavaScript array reference library for individual array operations and boundary tests to check a rule independently.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: Build an Accessible Toggle →