Phase 11 — Events (Very Important)
Module 38.2 — Event Listeners & Event Methods
Modern JavaScript applications rely heavily on Event Listeners. They allow applications to respond to user actions such as clicks, typing, scrolling, and form submissions. Understanding how events propagate through the DOM and how to control that propagation is essential for building scalable and maintainable web applications.
Event Listener Flow
1User Action 2 │ 3 ▼ 4Browser Detects Event 5 │ 6 ▼ 7Event Listener 8 │ 9 ▼ 10Callback Function 11 │ 12 ▼ 13DOM Updated
Overview
| Method | Purpose |
|---|---|
addEventListener() | Attach an event listener |
removeEventListener() | Remove an event listener |
preventDefault() | Prevent the browser's default behavior |
stopPropagation() | Stop event bubbling or capturing |
stopImmediatePropagation() | Stop propagation and remaining listeners |
| Event Bubbling | Event travels upward |
| Event Capturing | Event travels downward |
| Event Delegation | Handle child events using a parent |
| Event Options | once, capture, passive |
addEventListener()
Registers an event listener on an element.
Syntax
1element.addEventListener(event, callback, options);
Example
1<button id="btn"> 2 Click Me 3</button>
1const button = document.getElementById("btn"); 2 3button.addEventListener("click", () => { 4 5 console.log("Button Clicked"); 6 7});
Unlike inline events (onclick), addEventListener() allows multiple listeners on the same element.
Multiple Event Listeners
1button.addEventListener("click", () => { 2 3 console.log("First"); 4 5}); 6 7button.addEventListener("click", () => { 8 9 console.log("Second"); 10 11});
Output
1First 2Second
Named Event Handler
1function greet() { 2 3 alert("Welcome!"); 4 5} 6 7button.addEventListener("click", greet);
Using named functions is useful when you want to remove the listener later.
removeEventListener()
Removes a previously registered event listener.
1function showMessage() { 2 3 console.log("Clicked"); 4 5} 6 7button.addEventListener("click", showMessage); 8 9button.removeEventListener("click", showMessage);
Anonymous functions cannot be removed because they do not have a reusable reference.
Incorrect Usage
❌ Wrong
1button.addEventListener("click", function () { 2 3 console.log("Hello"); 4 5}); 6 7button.removeEventListener("click", function () { 8 9 console.log("Hello"); 10 11});
Each anonymous function is a different object, so the listener is not removed.
preventDefault()
Stops the browser's default action.
Example — Form Submission
1<form id="login"> 2 3 <button> 4 5 Submit 6 7 </button> 8 9</form>
1const form = document.getElementById("login"); 2 3form.addEventListener("submit", (event) => { 4 5 event.preventDefault(); 6 7 console.log("Form Submitted"); 8 9});
Without preventDefault(), the browser reloads the page after form submission.
Another Example
Prevent a link from opening.
1document.querySelector("a") 2 3.addEventListener("click", (event) => { 4 5 event.preventDefault(); 6 7});
stopPropagation()
Stops the event from moving to parent elements.
HTML
1<div id="parent"> 2 3 <button id="child"> 4 5 Click 6 7 </button> 8 9</div>
JavaScript
1const parent = document.getElementById("parent"); 2 3const child = document.getElementById("child"); 4 5parent.addEventListener("click", () => { 6 7 console.log("Parent"); 8 9}); 10 11child.addEventListener("click", (event) => { 12 13 event.stopPropagation(); 14 15 console.log("Child"); 16 17});
Output
1Child
The parent's listener does not execute.
stopImmediatePropagation()
Stops:
- Event propagation
- Remaining listeners on the same element
1button.addEventListener("click", (event) => { 2 3 event.stopImmediatePropagation(); 4 5 console.log("First"); 6 7}); 8 9button.addEventListener("click", () => { 10 11 console.log("Second"); 12 13});
Output
1First
The second listener never runs.
Event Bubbling
By default, events bubble from the target element to its ancestors.
1button 2 ▲ 3div 4 ▲ 5body 6 ▲ 7document
Example
1parent.addEventListener("click", () => { 2 3 console.log("Parent"); 4 5}); 6 7child.addEventListener("click", () => { 8 9 console.log("Child"); 10 11});
Output
1Child 2Parent
Event Capturing
Capturing processes events from the outermost ancestor toward the target.
1parent.addEventListener( 2 3 "click", 4 5 () => { 6 7 console.log("Parent"); 8 9 }, 10 11 true 12 13);
Execution Order
1Parent 2Child
Bubbling vs Capturing
| Bubbling | Capturing |
|---|---|
| Default behavior | Optional |
| Target → Parent | Parent → Target |
false | true |
Event Delegation
Instead of attaching listeners to every child element, attach one listener to the parent.
HTML
1<ul id="menu"> 2 3 <li>Home</li> 4 5 <li>About</li> 6 7 <li>Contact</li> 8 9</ul>
JavaScript
1const menu = document.getElementById("menu"); 2 3menu.addEventListener("click", (event) => { 4 5 if (event.target.tagName === "LI") { 6 7 console.log(event.target.textContent); 8 9 } 10 11});
Benefits:
- Better performance
- Less memory usage
- Works with dynamically added elements
Event Listener Options
once
Runs only one time.
1button.addEventListener( 2 3 "click", 4 5 () => { 6 7 console.log("Clicked"); 8 9 }, 10 11 { 12 13 once: true 14 15 } 16 17);
capture
Runs during the capturing phase.
1button.addEventListener( 2 3 "click", 4 5 handler, 6 7 { 8 9 capture: true 10 11 } 12 13);
passive
Improves scroll performance.
1window.addEventListener( 2 3 "scroll", 4 5 () => { 6 7 console.log("Scrolling"); 8 9 }, 10 11 { 12 13 passive: true 14 15 } 16 17);
Use passive listeners when the event handler does not call preventDefault().
Practical Example — Prevent Form Refresh
1<form id="signup"> 2 3 <input placeholder="Email"> 4 5 <button> 6 7 Register 8 9 </button> 10 11</form>
1const form = document.getElementById("signup"); 2 3form.addEventListener("submit", (event) => { 4 5 event.preventDefault(); 6 7 alert("Registration Successful"); 8 9});
Real-World Example — Event Delegation
1const table = document.querySelector("table"); 2 3table.addEventListener("click", (event) => { 4 5 const cell = event.target.closest("td"); 6 7 if (!cell) return; 8 9 cell.classList.toggle("selected"); 10 11});
This pattern is commonly used in:
- Data Tables
- File Managers
- Email Clients
- Task Boards
Mini Project — Dynamic Todo List
HTML
1<input id="task"> 2 3<button id="add"> 4 5Add 6 7</button> 8 9<ul id="list"></ul>
JavaScript
1const input = document.getElementById("task"); 2const button = document.getElementById("add"); 3const list = document.getElementById("list"); 4 5button.addEventListener("click", () => { 6 7 if (input.value.trim() === "") return; 8 9 const item = document.createElement("li"); 10 11 item.textContent = input.value; 12 13 list.appendChild(item); 14 15 input.value = ""; 16 17}); 18 19list.addEventListener("click", (event) => { 20 21 if (event.target.tagName === "LI") { 22 23 event.target.remove(); 24 25 } 26 27});
This project demonstrates:
addEventListener()- Event Delegation
- Dynamic Elements
- DOM Manipulation
Common Mistakes
Using onclick Instead of addEventListener()
❌ Avoid
1button.onclick = handler;
This replaces any existing click handler.
✅ Prefer
1button.addEventListener("click", handler);
Forgetting preventDefault()
Without it, forms submit and links navigate using the browser's default behavior.
Attaching Too Many Listeners
Instead of:
1items.forEach(item => { 2 3 item.addEventListener("click", handler); 4 5});
Use event delegation on the parent container.
Best Practices
- Always prefer
addEventListener()over inline HTML event attributes. - Use named functions if you need to remove listeners later.
- Use
preventDefault()only when you intentionally want to override the browser's default behavior. - Use
stopPropagation()sparingly, as it can interfere with other event handlers. - Use event delegation for lists, tables, and dynamically created elements.
- Consider
oncefor one-time actions andpassivefor performance-sensitive events such as scrolling.
Interview Questions
Q1. Why is addEventListener() preferred over onclick?
It supports multiple listeners, provides options like once and capture, and keeps JavaScript separate from HTML.
Q2. What does preventDefault() do?
It prevents the browser's default action for an event, such as submitting a form or following a link.
Q3. What is the difference between stopPropagation() and stopImmediatePropagation()?
stopPropagation()stops the event from reaching ancestor elements.stopImmediatePropagation()also prevents any remaining listeners on the same element from running.
Q4. What is event delegation?
Event delegation is a technique where a single event listener is attached to a parent element to handle events from its child elements using event bubbling.
Q5. What is the difference between event bubbling and capturing?
- Bubbling: Event travels from the target element up through its ancestors.
- Capturing: Event travels from the document down to the target element before bubbling.
Summary
In this module, you learned how to work with JavaScript event listeners and event propagation. You explored addEventListener(), removeEventListener(), preventDefault(), stopPropagation(), stopImmediatePropagation(), event bubbling, capturing, delegation, and listener options such as once, capture, and passive. These concepts are fundamental for building scalable, high-performance, and interactive web applications.
Next Module: Module 38.3 — Custom Events & Advanced Event Handling, where you'll learn
dispatchEvent(),CustomEvent, custom event data, event-driven architecture, component communication, and advanced event patterns used in modern JavaScript applications.