Events
Events are actions or occurrences that happen in the system you are programming, which the system tells you about so your code can react to them. For example, a user clicking a button, hovering over an element, or submitting a form.
1. Event Listeners (addEventListener)
The most robust way to handle events in modern JavaScript is using the addEventListener method.
const button = document.querySelector('button');
button.addEventListener('click', () => {
console.log('Button was clicked!');
});
Why not onclick?
While you can use element.onclick = function() {}, addEventListener is superior because:
- You can attach multiple handlers to the same event on the same element.
- It gives you finer control over the execution phase (capturing vs. bubbling).
2. The Event Object
When an event occurs, an Event object is passed to the handler function. It contains rich information about the event.
document.addEventListener('keydown', (event) => {
console.log(`Key pressed: ${event.key}`);
console.log(`Ctrl key was held down: ${event.ctrlKey}`);
});
Common properties include target (the element that triggered the event), type (the event type, e.g., 'click'), and coordinates like clientX / clientY.
3. Event Propagation: Bubbling and Capturing
When an event fires on an element that has parent elements (e.g., a <button> inside a <div> inside the <body>), modern browsers run through three phases:
- Capturing Phase: The event goes down from the
Windowdown through the DOM tree to the target element's parent. - Target Phase: The event reaches the target element itself.
- Bubbling Phase: The event bubbles up from the target element's parent back up to the
Window.
By default, addEventListener registers handlers in the Bubbling phase.
<div id="parent">
<button id="child">Click Me</button>
</div>
// Bubbling (Default)
document.getElementById('child').addEventListener('click', () => console.log('Child clicked!'));
document.getElementById('parent').addEventListener('click', () => console.log('Parent clicked!'));
// Output: "Child clicked!" then "Parent clicked!"
// Capturing (pass true as the third argument)
document.getElementById('parent').addEventListener('click', () => console.log('Parent capturing!'), true);
4. stopPropagation()
If you want to prevent an event from bubbling up the DOM tree, you can use event.stopPropagation().
document.getElementById('child').addEventListener('click', (e) => {
e.stopPropagation();
console.log('Child clicked! Parent will not hear this.');
});
5. Event Delegation
Event delegation is a pattern that takes advantage of Event Bubbling. Instead of attaching event listeners to every single child element (which can be heavy on memory if there are many children), you attach a single listener to the parent element.
<ul id="todo-list">
<li>Buy groceries</li>
<li>Clean room</li>
<li>Walk dog</li>
</ul>
const list = document.getElementById('todo-list');
list.addEventListener('click', (event) => {
// Check if the clicked element was an LI
if (event.target.tagName === 'LI') {
event.target.style.textDecoration = 'line-through';
}
});
This is incredibly useful for dynamically added elements, as the parent listener will automatically handle clicks on new children.
6. preventDefault()
Many HTML elements have default behaviors. For example, a <form> submits data and refreshes the page, and an <a> tag navigates to a new URL. You can stop this using event.preventDefault().
const form = document.querySelector('form');
form.addEventListener('submit', (event) => {
event.preventDefault(); // Stops the page from refreshing
// Custom validation and fetch API logic here
console.log('Form submitted via JavaScript!');
});