Event Listeners
Events are things that happen in the browser — clicks, key presses, form submissions, page loads. JavaScript lets you "listen" for these events and run code when they occur.
// Adding event listeners:
const button = document.querySelector("#myButton");
button.addEventListener("click", function(event) {
console.log("Button clicked!", event);
});
// Arrow function (no 'this' binding):
button.addEventListener("click", (e) => {
console.log("Clicked at:", e.clientX, e.clientY);
});
// Named function (easier to remove later):
function handleClick(e) {
console.log("Handled!");
}
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick); // remove it
// Options object:
button.addEventListener("click", handler, {
once: true, // fire only once, then auto-remove
passive: true, // won't call preventDefault (performance)
capture: true // fire in capture phase (rare)
});
The Event Object
document.addEventListener("click", (event) => {
// Common event properties:
event.type; // "click"
event.target; // element that was clicked
event.currentTarget; // element the listener is attached to
event.clientX; // click X position in viewport
event.clientY; // click Y position in viewport
event.pageX; // click X relative to document
event.timeStamp; // when the event fired (ms)
// Prevent default behavior:
event.preventDefault(); // e.g., prevent link navigation
// Stop propagation:
event.stopPropagation(); // don't bubble to parent elements
});
// Keyboard events:
document.addEventListener("keydown", (e) => {
e.key; // "Enter", "Escape", "a", "ArrowLeft"
e.code; // "Enter", "Escape", "KeyA", "ArrowLeft"
e.ctrlKey; // true if Ctrl held
e.shiftKey; // true if Shift held
e.altKey; // true if Alt held
e.metaKey; // true if Cmd/Win key held
if (e.key === "Escape") closeModal();
if (e.ctrlKey && e.key === "s") saveDocument(e);
});
// Form events:
form.addEventListener("submit", (e) => {
e.preventDefault(); // ALWAYS prevent default on forms you handle!
const data = new FormData(e.target);
const name = data.get("name");
});
Event Bubbling & Delegation
// Event bubbling: events fire on target, then bubble up to parents
// Click on inside inside :
// 1. span handler fires
// 2. div handler fires
// 3. body handler fires
// 4. document handler fires
// Event delegation — attach ONE listener to parent, handle multiple children:
const list = document.querySelector("ul");
list.addEventListener("click", (e) => {
// e.target is the actual clicked element
if (e.target.tagName === "LI") {
e.target.classList.toggle("completed");
}
});
// Why delegation is better than adding listener to each item:
// - Works for dynamically added elements!
// - Better performance (one listener vs many)
// - Less memory usage
// Practical delegation with closest():
document.querySelector(".product-list").addEventListener("click", (e) => {
const btn = e.target.closest("[data-action]");
if (!btn) return;
const action = btn.dataset.action;
const productId = btn.closest(".product-card").dataset.id;
if (action === "add-to-cart") addToCart(productId);
if (action === "wishlist") addToWishlist(productId);
});
Common Events Reference
Event When It Fires Common Use
click Mouse click Buttons, links
dblclick Double click Edit mode
mouseover/out Hover enter/leave Tooltips
keydown/up Key pressed/released Shortcuts, input
input Value changes Real-time validation
change Input loses focus Select, checkbox
submit Form submitted Form handling
focus/blur Element gets/loses focus Input validation
scroll Page scrolls Sticky nav, lazy load
resize Window resized Responsive JS
DOMContentLoaded DOM ready Init code
load Page fully loaded Images loaded
⚡ Key Takeaways
- Use
addEventListener not inline onclick — it supports multiple listeners
event.target = element that triggered event; currentTarget = element with listener
- Always
preventDefault() on forms you handle manually
- Event delegation: attach one listener to parent, handle children via
e.target
- Use
{ once: true } for one-time events — auto-removes the listener
- Events bubble up from child to parent —
stopPropagation() cancels bubbling
🎯 Practice Exercises
EXERCISE 1
Build a keyboard shortcut handler: Ctrl+S calls save(), Ctrl+Z calls undo(), Escape closes the current modal. Use one keydown listener on document.
EXERCISE 2 — CHALLENGE
Build a dynamic todo list using event delegation: one click listener on the list handles adding, completing, and deleting items (via data-action attributes).