Event Listeners & Event Delegation
Events are signals fired by the browser when users interact with the webpage (e.g. clicking buttons, typing, submitting forms). JavaScript listens for these events and responds by running callback functions.
1. Listening to Events
We attach event listeners using addEventListener():
// Syntax: element.addEventListener(eventName, callbackFunction)
const btn = document.querySelector('#submit-btn');
function handleClick(event) {
console.log("Button clicked!");
}
btn.addEventListener('click', handleClick);Removing Event Listeners
To remove an event listener via removeEventListener(), you must pass a reference to the same named function used when adding it. Anonymous functions cannot be removed:
btn.removeEventListener('click', handleClick); // Works!2. The Event Object
When an event fires, the browser automatically passes an Event Object (e) to the callback function. It contains metadata about the interaction:
e.target: The actual HTML element that triggered the event (great for determining which item was clicked).e.type: The name of the event (e.g. "click", "submit").e.preventDefault(): Prevents the browser's default behavior for that event (such as stopping a form submission from reloading the page).
const form = document.querySelector('form');
form.addEventListener('submit', (e) => {
e.preventDefault(); // Page will not reload
console.log("Form data processed via JS instead!");
});e.stopPropagation(): Stops the event from propagating further up the DOM tree.
3. Event Propagation: Bubbling & Capturing
When an event triggers on an element, it travels through the DOM tree. This is called event propagation, which has three phases:
- Capturing Phase: The event trickles down from the
windowto the target element. - Target Phase: The event reaches the target element.
- Bubbling Phase (Default): The event bubbles back up from the target element to the
window.
<div class="parent">
<button class="child">Click Me</button>
</div>If you click the <button> and both elements have click listeners:
- The button's listener fires first.
- The div's listener fires second (because the click event bubbles up to the parent).
4. Event Delegation
Binding individual event listeners to hundreds of list items or buttons consumes heavy browser memory and fails to apply to dynamically added elements.
Event Delegation resolves this. Instead of attaching a listener to every child, you attach a single listener to a common parent element. When a child is clicked, the event bubbles up to the parent, and you use e.target to determine which child triggered it.
<ul id="todo-list">
<li>Buy milk <button class="delete-btn">X</button></li>
<li>Code JS <button class="delete-btn">X</button></li>
</ul>const list = document.querySelector('#todo-list');
list.addEventListener('click', (e) => {
// Check if the clicked element has the class 'delete-btn'
if (e.target.classList.contains('delete-btn')) {
const li = e.target.parentElement;
li.remove(); // Deletes the list item
console.log("Deleted todo task!");
}
});Advantages of Event Delegation:
- Low Memory Overhead: One event listener instead of dozens.
- Handles Dynamic Elements: If you append a new
<li>to the list, the delete button will automatically work without needing to bind new event listeners.
In the next guide, we will learn how to dynamically manipulate styles and CSS classes using JavaScript.