Event handling
A mechanism for running code in response to user actions or particular situations.
Adding an event listener
const button = document.querySelector("button");
// An event listener using an arrow function
button.addEventListener("click", () => {
console.log("The button was clicked!");
});
// An event listener using a named function
function handleClick(event) {
// You can get information from the event object
console.log("Click position:", event.clientX, event.clientY);
}
button.addEventListener("click", handleClick);
// Removing an event listener (pass the same function used when registering)
button.removeEventListener("click", handleClick);
Listener options
You can pass options as the third argument of addEventListener.
// once: runs only once and is removed automatically
button.addEventListener("click", () => {
console.log("Reacts only to the first click");
}, { once: true });
// passive: declares that you won't call preventDefault(), improving scroll performance
// (Effective for touchstart / touchmove / wheel. Not needed for scroll itself, since it can't be canceled.)
element.addEventListener("touchmove", handleTouchMove, { passive: true });
Common event types
// Mouse events
element.addEventListener("click", handler); // On click
element.addEventListener("dblclick", handler); // On double-click
element.addEventListener("mouseover", handler); // When the mouse enters the element
element.addEventListener("mouseout", handler); // When the mouse leaves the element
// Keyboard events
document.addEventListener("keydown", handler); // When a key is pressed
document.addEventListener("keyup", handler); // When a key is released
// keypress is deprecated. For detecting character input, use keydown or input as well
// Form events
form.addEventListener("submit", handler); // When the form is submitted
input.addEventListener("input", handler); // While typing
input.addEventListener("change", handler); // When the input value is changed
// Document and window events
window.addEventListener("load", handler); // When the page finishes loading
document.addEventListener("DOMContentLoaded", handler); // When the DOM is fully built
window.addEventListener("resize", handler); // When the window is resized
Event propagation and bubbling
An element's event passes through the following 3 phases in order.
Normally addEventListener catches events in the bubbling phase (③).
// Stopping bubbling
element.addEventListener("click", (event) => {
event.stopPropagation();
console.log("The event stops here");
});
// Preventing the default behavior (such as stopping a form submission's page navigation)
form.addEventListener("submit", (event) => {
event.preventDefault();
console.log("The form will not be submitted");
});
If you pass { capture: true } as the third argument of addEventListener, you can catch events in the capturing phase (top to bottom) rather than during bubbling (bottom to top). Bubbling is usually enough, but use this when you want a parent element to handle the event first.
Event delegation
A technique where you set just one listener on a parent element and handle the events of many child elements together. It takes advantage of bubbling.
const list = document.querySelector("ul");
list.addEventListener("click", (event) => {
// Using closest, you can correctly get the li even when a child element inside the li is clicked
const item = event.target.closest("li");
if (item) {
console.log("Clicked list item:", item.textContent);
}
});
Rather than setting a listener individually on 100 list items, setting one on the whole list is more efficient. Using event.target.closest("li"), you can reliably get the parent li even when there's an icon or text inside the list item.
What to read next
- Asynchronous processing — Cleanly write "processing that waits," such as clicks and communication