Skip to content

Events

Events are actions or things that happen in the browser, like clicking, typing, scrolling, or a page loading.

JavaScript listens for these events and runs code in response, which is what makes interactive applications possible.

graph TD A[User Action] --> B[Event Created] B --> C[Capture Phase] C --> D[Target Phase] D --> E[Bubble Phase]
  • Capture Phase -> the event travels from the root all the way down to the target.
  • Target Phase -> the event actually reaches the target element.
  • Bubble Phase -> the event then travels back up from the target to the root.
const btn = document.querySelector("#btn");
function handleClick() {
console.log("Clicked");
}
btn.addEventListener("click", handleClick);
btn.addEventListener("click", handleClick, {
once: true,
capture: false,
passive: true
});
  • once -> makes the listener run only one single time.
  • capture -> makes the listener run during the capture phase.
  • passive -> helps improve scrolling performance.
btn.removeEventListener("click", handleClick);
input.addEventListener("input", (event) => {
console.log(event.target.value);
console.log(event.type);
});

target

The actual element where the event really happened.

currentTarget

The element that the listener was actually attached to.

type

The kind of event it is, like click or input.

parent.addEventListener("click", (e) => {
console.log(e.target); // actual clicked element
console.log(e.currentTarget); // parent
});

Mouse

click, dblclick, mousemove

Keyboard

keydown, keyup

Form

submit, input, change

Window

load, resize, scroll

form.addEventListener("submit", (e) => {
e.preventDefault();
});
child.addEventListener("click", (e) => {
e.stopPropagation();
});

preventDefault() stops the browser’s normal, built-in behavior (like a form actually submitting and reloading the page). stopPropagation() stops the event from continuing to travel further, either up or down, through the other elements.

graph TD A[Window] --> B[Document] B --> C[Parent] C --> D[Target] D --> E[Bubble Up]
list.addEventListener("click", (e) => {
if (e.target.matches("li")) {
console.log(e.target.textContent);
}
});
  • Fewer listeners -> means better performance.
  • It also works fine even with elements that are added later, dynamically.
graph LR A[Parent Listener] --> B[Child Events]
setTimeout(() => {
console.log("Runs once");
}, 2000);

setTimeout runs some code just once, after a delay. setInterval keeps running that code again and again, on repeat, until you stop it using clearInterval.

graph TD A[Call Stack] --> B{Empty?} B -->|No| C[Wait] B -->|Yes| D[Take Event Callback] D --> E[Execute]
form.addEventListener("submit", (e) => {
e.preventDefault();
const data = new FormData(form);
console.log(data.get("email"));
});
btn.addEventListener("click", async () => {
btn.disabled = true;
await new Promise(r => setTimeout(r, 1000));
btn.disabled = false;
});

This pattern is often used to stop a user from clicking a button multiple times in a row, like while a form is being submitted.

element.removeEventListener("click", handler);
clearInterval(timerId);

It is good practice to clean up listeners and timers once you no longer need them, so you do not waste memory.

  1. Use addEventListener instead of writing inline handlers directly in your HTML.
  2. Prefer event delegation when working with lists.
  3. Keep your handler functions small and simple.
  4. Clean up listeners once they are no longer needed.
  5. Avoid doing unnecessary DOM queries inside your handlers.