~/
hackweb.dev
JavaScript Event Delegation & Bubbling
Quiz
⌘K
...
~/
/tutorials
/js/js-event-delegation/edit
~ Contribute
Suggest a correction or improvement. The author reviews it before it goes live.
Loading...
Comment
0 / 300
Typo
Grammar
Broken link
Clarify
Code
en/tutorials/js/19js-event-delegation
Write
Preview
Diff
# JavaScript Event Delegation & Bubbling Events bubble up from the target element to the root. This enables efficient event handling. ## Event Bubbling ```javascript let parent = document.getElementById("parent"); let child = document.getElementById("child"); child.addEventListener("click", () => console.log("Child clicked")); parent.addEventListener("click", () => console.log("Parent clicked")); // Clicking child logs both — events bubble up ``` ## Stopping Propagation ```javascript child.addEventListener("click", (e) => { e.stopPropagation(); // Stops bubbling console.log("Child only"); }); ``` ## Event Target vs Current Target ```javascript list.addEventListener("click", (e) => { console.log(e.target); // The actual clicked element console.log(e.currentTarget); // The element with the listener }); ``` **Remember:** `e.target` = what was clicked. `e.currentTarget` = where the listener is. ## Event Delegation Add one listener to the parent instead of one per item: ```javascript // Bad: One listener per item document.querySelectorAll(".item").forEach(item => { item.addEventListener("click", handler); }); // Good: One listener on parent document.getElementById("list").addEventListener("click", (e) => { if (e.target.classList.contains("item")) handler(e); }); ``` **Why delegation matters:** New items added later work automatically — no re-attaching listeners. ## Using closest() and matches() ```javascript list.addEventListener("click", (e) => { if (e.target.matches(".item")) handler(e); }); // Use closest() when clickable element has children list.addEventListener("click", (e) => { let item = e.target.closest(".item"); if (!item) return; handler(item); }); ``` **Tip:** `closest()` is more reliable when the target has nested child elements. ## Complete Example: Todo List ```javascript let todoList = document.getElementById("todo-list"); let input = document.getElementById("todo-input"); let addBtn = document.getElementById("add-btn"); addBtn.addEventListener("click", () => { let text = input.value.trim(); if (!text) return; let li = document.createElement("li"); li.innerHTML = `<span class="text">${text}</span><button class="delete">Delete</button>`; todoList.appendChild(li); input.value = ""; }); todoList.addEventListener("click", (e) => { if (e.target.classList.contains("text")) { e.target.parentElement.classList.toggle("completed"); } if (e.target.classList.contains("delete")) { e.target.parentElement.remove(); } }); ``` ## Best Practices - **Use delegation for dynamic content** — New elements work automatically - **Use `closest()` for parent matching** — More reliable with nested elements - **Use `matches()` for checking** — Clean selector matching - **Stop propagation when needed** — Prevent unintended parent handlers - **Use data attributes** — Store extra info on elements ## Common Mistakes - **Not checking `e.target`** — Handle all clicks unintentionally - **Forgetting `stopPropagation()`** — Unintended side effects - **Using delegation everywhere** — Simple cases don't need it - **Ignoring keyboard events** — Accessibility matters - **Not cleaning up listeners** — Remove when no longer needed
No changes yet
Reset to original
Submit suggestion
cancel