In this chapter, we’ll go deeper into how JavaScript handles events behind the scenes, including:
event.target vs event.currentTargetUnderstanding these concepts is essential for building scalable, dynamic UIs — especially in lists, tables, forms, and SPAs.
By default, events bubble up the DOM tree — starting from the target element and moving to its ancestors.
<div id="parent">
<button id="child">Click Me</button>
</div>document.getElementById("child").addEventListener("click", () => {
console.log("Child clicked");
});
document.getElementById("parent").addEventListener("click", () => {
console.log("Parent clicked");
});Output if you click the button:
Child clicked
Parent clicked👉 This is because the event “bubbles” from child to parent. Know more about event bubbles.
Use event.stopPropagation() to prevent the event from bubbling up.
document.getElementById("child").addEventListener("click", function (event) {
event.stopPropagation();
console.log("Only child clicked");
});Now, clicking the button won’t trigger the parent’s listener.
Capturing is the opposite of bubbling: the event goes top → bottom in the DOM before hitting the target.
Enable capturing with the 3rd parameter of addEventListener():
element.addEventListener("click", handler, true);document.getElementById("parent").addEventListener("click", () => {
console.log("Parent Capturing");
}, true);
document.getElementById("child").addEventListener("click", () => {
console.log("Child Bubble");
});Capturing runs first (if enabled), then bubbling.
event.target: The actual element that triggered the eventevent.currentTarget: The element the handler is attached todocument.getElementById("parent").addEventListener("click", function (e) {
console.log("Target:", e.target); // What was clicked
console.log("Current Target:", e.currentTarget); // #parent
});Instead of adding event listeners to multiple child elements, add one listener to their parent and check which child triggered it using event.target.
Great for dynamic content like to-do lists or comment sections.
<ul id="myList">
<li>Item 1</li>
<li>Item 2</li>
</ul>document.getElementById("myList").addEventListener("click", function (e) {
if (e.target.tagName === "LI") {
alert("You clicked " + e.target.textContent);
}
});This works even if new <li> items are added later — no need to bind again!
button.addEventListener("click", function () {
alert("You’ll see me only once!");
}, { once: true });<ul id="tasks">
<li>Task 1 <button>Delete</button></li>
<li>Task 2 <button>Delete</button></li>
</ul>document.getElementById("tasks").addEventListener("click", function (e) {
if (e.target.tagName === "BUTTON") {
e.target.parentElement.remove(); // Remove <li>
}
});This is cleaner and more efficient than assigning listeners to every button individually.
stopPropagation.{ once: true }.target and currentTarget and observe the difference.