Module 2: Events
Event Listeners
let button = document.querySelector("#myBtn");
button.addEventListener("click", function () {
console.log("Button was clicked!");
});
button.addEventListener("click", () => console.log("Clicked!"));
function handleClick() {
console.log("Handled");
}
button.addEventListener("click", handleClick);
button.removeEventListener("click", handleClick);
Multiple listeners can be attached to the same element for the same event — all of them run.
Bubbling, Capturing & Delegation
When you click a child element, the click event doesn't just fire there — it travels through the DOM tree in two phases:
Capturing phase: document → ... → target (top-down)
Target phase: the element actually clicked
Bubbling phase: target → ... → document (bottom-up, the default)
outer.addEventListener("click", () => console.log("Outer"));
inner.addEventListener("click", () => console.log("Inner"));
// Clicking "inner" logs: "Inner", then "Outer" (bubbles up)
// Listen during the capturing phase instead, with a third argument
outer.addEventListener("click", () => console.log("Outer capturing"), true);
// Stop an event from bubbling further
inner.addEventListener("click", (event) => event.stopPropagation());
Event delegation uses bubbling on purpose: instead of attaching a listener to every item in a list, attach ONE listener to their shared parent and check which child triggered it. Efficient, and automatically works for items added later.
let list = document.querySelector("ul");
list.addEventListener("click", function (event) {
if (event.target.tagName === "LI") {
console.log("You clicked:", event.target.textContent);
}
});
Keyboard, Mouse & Touch Events
// Mouse
el.addEventListener("click", fn);
el.addEventListener("dblclick", fn);
el.addEventListener("mouseover", fn);
el.addEventListener("mouseout", fn);
// Keyboard
input.addEventListener("keydown", (e) => console.log(e.key)); // e.g. "Enter", "a"
input.addEventListener("keyup", fn);
// Touch (mobile)
el.addEventListener("touchstart", fn);
el.addEventListener("touchmove", fn);
el.addEventListener("touchend", fn);
// Every handler receives an event object with useful details
button.addEventListener("click", function (event) {
console.log(event.type); // "click"
console.log(event.target); // the exact element clicked
});
Forms & Validation
let form = document.querySelector("form");
form.addEventListener("submit", function (event) {
event.preventDefault(); // stops the default page-reload behavior
let email = document.querySelector("#email").value;
if (!email.includes("@")) {
console.log("Please enter a valid email");
return;
}
console.log("Form submitted:", email);
});
// HTML also provides built-in validation attributes (checked before JS even runs)
// <input type="email" required minlength="5">
input.addEventListener("input", (e) => {
console.log("Current value:", e.target.value); // fires on every keystroke
});