Event Handling
Events connect user actions—clicks, key presses, scrolling—to your JavaScript logic. Today you’ll learn how to add and remove listeners, access event data, control propagation, and make your UI feel responsive.
Step 1: Adding Event Listeners
const button = document.querySelector(".cta");
button.addEventListener("click", () => {
console.log("Button clicked!");
});addEventListenertakes the event type, callback, and optional options.- Avoid inline
onclickattributes; listeners keep markup and logic separate.
Step 2: Using the Event Object
button.addEventListener("click", (event) => {
console.log(event.type); // "click"
console.log(event.target); // element that fired the event
});- The event object carries metadata like coordinates, key codes, modifier keys, and more.
- Use
event.targetto access the element that triggered the listener.
Step 3: Preventing Default Behavior
const form = document.querySelector("form");
form.addEventListener("submit", (event) => {
event.preventDefault();
console.log("Form submission blocked while we validate.");
});preventDefault()stops the browser’s default action (e.g., navigating on anchor click, submitting forms).- Call it when you need to handle logic manually before allowing the default behavior.
Step 4: Event Propagation
- Events bubble from the target up through ancestors (
target → parent → document). - They also support capturing (top-down) if you pass
{ capture: true }.
document.body.addEventListener("click", () => {
console.log("Body clicked");
});- Use propagation to handle many child elements with a single listener.
Step 5: Delegation Pattern
const list = document.querySelector(".task-list");
list.addEventListener("click", (event) => {
const item = event.target.closest("li");
if (!item) return;
item.classList.toggle("done");
});- Listen on a parent element and act on matching child targets.
- Great for dynamic lists where items are added or removed.
Step 6: Removing Listeners
function handleClick() {
console.log("Clicked once");
button.removeEventListener("click", handleClick);
}
button.addEventListener("click", handleClick);- Use named functions so you can remove them later.
- Removing listeners prevents memory leaks in long-lived components.
Step 7: Keyboard and Input Events
const input = document.querySelector("#search");
input.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
console.log(`Searching for ${input.value}`);
}
});- Check
event.key,event.code, or modifier properties (event.shiftKey). - Use
inputorchangeevents to react to value updates.
Step 8: Passive and Once Options
window.addEventListener(
"scroll",
() => {
// update header shadow
},
{ passive: true }
);
button.addEventListener(
"click",
() => console.log("Fire once"),
{ once: true }
);passive: truehints that the listener won’t callpreventDefault; improves scroll performance.once: trueauto-removes the listener after the first call.
Step 9: Practice Prompts
- Add a click listener to a button that toggles a
"visible"class on a modal. - Use event delegation on
.todo-listso clicking any<li>toggles"completed". - Block form submission until an input has at least three characters; show an error message.
- Implement a keydown handler that closes a dialog when
Escapeis pressed. - Listen for
scrollevents and add a"scrolled"class to the header once the page offset passes 80px.
Key Takeaways
- ✅
addEventListeneris the standard way to connect events to logic. - ✅ Event objects provide context like target elements and key details.
- ✅ Propagation enables delegation; stop it with
stopPropagation()when necessary. - ✅ Options like
onceandpassivefine-tune performance and lifecycle.
🎯 Quick Check
- Why is
addEventListenerpreferred over inline event attributes? - How does event delegation reduce the number of listeners on a page?
- When would you call
event.preventDefault()? - What do the
onceandpassivelistener options do?
Next lesson: apply everything you’ve learned to build an interactive gallery. 🖼️