One listener for a thousand buttons
Events don't just happen on the element you click — they bubble up through every ancestor. That means one listener on a list can handle clicks on all its items, even ones added later. This is event delegation.
Bubbling and delegation
const list = document.querySelector("#list");
list.addEventListener("click", (event) => {
const button = event.target.closest("button[data-id]");
if (!button) return;
console.log("Delete item", button.dataset.id);
button.closest("li").remove();
});
document.querySelector("#add").addEventListener("click", () => {
const id = Date.now();
list.insertAdjacentHTML("beforeend", `<li>New <button data-id="${id}">✕</button></li>`);
});- event.target — the element clicked. event.currentTarget — the element the listener is on.
- event.preventDefault() — cancel the browser's default (following a link, submitting a form).
- event.stopPropagation() — stop bubbling. Use rarely; it can break other code that relies on bubbling.