DOM Events in Depth

Course: JavaScript Intermediate

Lesson 10: DOM Events in Depth

Level

Intermediate

Estimated Reading Time

10–15 minutes

Goal

By the end of this lesson, the learner will be able to:

  • Attach and remove event listeners using addEventListener and removeEventListener
  • Access key properties on the event object such as target, type, and key
  • Prevent default browser behavior and stop event propagation when needed

Frontend Development Context

Events are how users interact with a webpage. Every click, keypress, form submission, and scroll is an event. The addEventListener approach is the standard for handling events in modern JavaScript — it is more flexible than inline HTML event attributes and allows multiple listeners on the same element.

Going deeper into events means understanding the event object that gets passed to your callback, knowing how to clean up listeners when they are no longer needed, and understanding propagation — the process by which events bubble up through the DOM tree. These skills are required for building forms, modals, keyboard shortcuts, and accessible interactive components.

Explanation

addEventListener(type, callback) attaches a function to an element that runs whenever the specified event occurs. The first argument is the event type as a string ("click", "keydown", "submit", "input", etc.). The second argument is the callback function that receives the event object.

The event object contains information about what happened: event.type is the event name, event.target is the element that triggered the event, event.key is the key pressed (for keyboard events), and event.preventDefault() stops the browser's default behavior (such as form submission or following a link).

removeEventListener removes a listener from an element. For this to work, the callback passed to removeEventListener must be the same function reference that was passed to addEventListener. Anonymous functions cannot be removed because there is no reference to them.

Events bubble by default — when a click happens on a child element, the click event travels up through each ancestor element. You can stop this with event.stopPropagation(). The third parameter of addEventListener can be { once: true } to automatically remove the listener after it fires once, or { capture: true } to listen during the capture phase instead of the bubble phase.

Code Example

This example adds click and keyboard listeners, reads the event object, and demonstrates removing a listener.

const button = document.querySelector("#submitBtn");
const input = document.querySelector("#searchInput");

// Named function so we can remove it later
function handleClick(event) {
  console.log("Event type:", event.type);       // "click"
  console.log("Clicked element:", event.target); // the button element
  event.target.textContent = "Clicked!";
}

button.addEventListener("click", handleClick);

// Keyboard event — reading event.key
input.addEventListener("keydown", function (event) {
  if (event.key === "Enter") {
    console.log("Search for:", input.value);
  }
  if (event.key === "Escape") {
    input.value = "";
    input.blur();
  }
});

// Remove the listener after 5 seconds
setTimeout(() => {
  button.removeEventListener("click", handleClick);
  console.log("Click listener removed");
}, 5000);

// Once option — fires only one time
button.addEventListener("mouseover", () => {
  button.style.background = "lightblue";
}, { once: true });

Code Explanation

handleClick is a named function stored in a variable so it can be passed to both addEventListener and later removeEventListener. If it were written as an anonymous function inline, removeEventListener would not work because there would be no reference to match.

The keydown listener reads event.key to determine which key was pressed. "Enter" triggers a search action, and "Escape" clears the input and removes focus. The key property gives you the human-readable key name, not a numeric code.

removeEventListener inside setTimeout removes the click handler after 5 seconds. This simulates a real-world pattern where you clean up event listeners to prevent memory leaks — for example, when a modal closes or a component unmounts.

The { once: true } option on the mouseover listener means the handler runs once and is automatically removed. This avoids the need to manually call removeEventListener when you only want a one-time effect.

Pattern Highlights

Positive Pattern: Use named functions when you need to remove an event listener later. Store the function in a variable so you have a reference to pass to removeEventListener.

⚠️ Neutral Note: Event bubbling allows a parent element to receive events from its children. This is usually the behavior you want, but call event.stopPropagation() when you need to prevent a child event from triggering a parent handler unintentionally.

Negative Pattern: Do not add event listeners inside a loop without a cleanup strategy. Adding listeners to many elements without removing them when they are no longer needed can cause memory leaks in long-running applications.

Common Mistakes

Mistake 1: Trying to remove an anonymous event listener

Using an anonymous function with removeEventListener — it never works.

button.addEventListener("click", () => console.log("clicked"));
button.removeEventListener("click", () => console.log("clicked")); // does nothing

Each () => creates a new function object. The one passed to removeEventListener is a different reference from the one attached. Use a named function stored in a variable instead.

Mistake 2: Forgetting event.preventDefault() on form submission

Allowing the browser to reload the page when a form is submitted.

form.addEventListener("submit", function (event) {
  // forgot event.preventDefault()
  validateForm(); // runs, but page reloads immediately and discards the result
});

Without event.preventDefault(), the browser performs its default form submission (a page reload or navigation). Call it at the top of the submit handler to keep the app in control.

Mistake 3: Accessing event.target properties without checking the element type

Assuming event.target is always the element you attached the listener to.

list.addEventListener("click", function (event) {
  console.log(event.target.dataset.id); // may be undefined if a child element was clicked
});

When a list item contains child elements (an icon, a span), clicking the child sets event.target to the child, not the list item. Check event.target.closest("li") or use event.currentTarget to reference the element the listener is attached to.

Quick Recap

  • addEventListener(type, callback) attaches an event handler; the callback receives an event object with details about what happened
  • Use named functions when you need to remove a listener with removeEventListener
  • event.target is the element that triggered the event; event.preventDefault() stops default browser behavior; event.stopPropagation() stops bubbling
  • The { once: true } option automatically removes the listener after it fires once

Ready to test your understanding? Take the quiz to reinforce what you learned.

© 2026 Ant Skillsv.26.07.07-23:01