W3docs

Event Handling in DOM

Learn JavaScript event handling in the DOM: addEventListener and removeEventListener, the event object, and preventDefault, with runnable examples.

Introduction to JavaScript Events

JavaScript events are actions or occurrences that happen in the browser — a click, a key press, a page finishing loading — that your code can detect and respond to. Reacting to events is what turns a static page into an interactive application.

This chapter covers the three ways to register event handlers, the most common event types, the event object that every handler receives, how events travel through the DOM (bubbling and capturing), and the patterns that keep event-heavy pages fast and leak-free.

Three Ways to Handle Events

Before looking at specific events, it helps to know that there are three distinct ways to attach a handler. They are listed from oldest to most flexible.

ApproachExampleMultiple handlers?Recommended
Inline HTML attribute<button onclick="doX()">NoNo — mixes markup and logic
DOM propertyelement.onclick = fnNo (last one wins)Only for simple cases
addEventListener()element.addEventListener("click", fn)YesYes — the modern standard
<button id="propBtn">Click me</button>

<script>
  const btn = document.getElementById("propBtn");

  // 1. DOM property — assigning again overwrites the previous handler
  btn.onclick = function () {
    alert("Handled by the onclick property");
  };

  // 2. addEventListener — adds without overwriting; you can attach many
  btn.addEventListener("click", function () {
    console.log("Also handled by addEventListener");
  });
</script>

Both handlers above run on a click. If you assign btn.onclick a second time, the first assignment is silently lost — which is the main reason addEventListener() is preferred. The rest of this chapter uses addEventListener().

Common Events in JavaScript

Click Event

The click event fires when the user presses and releases the primary mouse button (or taps, on touch devices) over an element. It is the most frequently used event.

<button id="clickButton">Click Me</button>

<script>
  document.getElementById("clickButton").addEventListener("click", function () {
    alert("Button was clicked!");
  });
</script>

In this example, an event listener is added to a button with the ID clickButton. When the button is clicked, an alert box with the message "Button was clicked!" will appear.

Mouseover Event

The mouseover event fires when the mouse pointer moves onto an element. (Its counterpart, mouseout, fires when the pointer leaves.)

<p id="mouseoverText">Hover over me!</p>

<script>
  document.getElementById("mouseoverText").addEventListener("mouseover", function () {
    this.style.color = "red";
  });
</script>

In this example, an event listener is added to a paragraph with the ID mouseoverText. When the mouse is hovered over the paragraph, its text color changes to red.

Keydown Event

The keydown event fires when the user presses a key while an element is focused. The handler receives an event object whose event.key property holds the value of the key.

<input type="text" id="inputField" placeholder="Type something..." />

<script>
  document.getElementById("inputField").addEventListener("keydown", function (event) {
    alert(`Key pressed: ${event.key}`);
    this.value = '';
  });
</script>

In this example, an event listener is added to an input field with the ID inputField. When a key is pressed while the input field is focused, the key pressed is displayed in an alert box.

The Event Object

Every handler is called with one argument: the event object. It describes what happened and exposes methods to control the event. The most useful members are:

  • event.target — the element the event actually originated on (e.g. the exact button clicked).
  • event.currentTarget — the element the listener is attached to. Inside a regular function this is the same as this.
  • event.type — the event name, such as "click".
  • event.preventDefault() — cancels the browser's default action (following a link, submitting a form).
  • event.stopPropagation() — stops the event from continuing to bubble up the DOM.
<a id="link" href="https://www.w3docs.com">Visit W3docs</a>

<script>
  document.getElementById("link").addEventListener("click", function (event) {
    event.preventDefault(); // stop the browser from navigating away
    console.log("type:", event.type);          // "click"
    console.log("target id:", event.target.id); // "link"
  });
</script>

Here preventDefault() keeps the page from navigating, so you can run your own logic instead — a common pattern for single-page apps and custom form validation.

Info

Inside an arrow function, this is not rebound to the element. Use event.currentTarget (or a regular function) when you need a reference to the element the listener is on. See arrow functions revisited for why.

Adding Event Listeners

The addEventListener() Method

The addEventListener() method attaches an event handler to an element without overwriting existing event handlers. This means you can attach multiple listeners — even for the same event type — to a single element. Its signature is element.addEventListener(type, handler, options), where options can fine-tune the behavior (once, capture, passive).

<button id="multiEventButton">Click or Hover</button>

<script>
  const button = document.getElementById("multiEventButton");

  button.addEventListener("click", function () {
    alert("Button clicked!");
  });

  button.addEventListener("mouseover", function () {
    button.style.backgroundColor = "lightblue";
  });
</script>

In this example, two event listeners are added to the button with the ID multiEventButton. One listener triggers an alert when the button is clicked, and the other changes the button's background color when the mouse hovers over it.

Info

Use addEventListener() to attach multiple event listeners to the same element without overwriting existing handlers.

Removing Event Listeners

The removeEventListener() Method

The removeEventListener() method detaches a handler that was attached with addEventListener(). The catch: you must pass the exact same function reference you added, which is why named functions are required to remove a listener later.

<button id="removeEventButton">Click Me</button>

<script>
  function showAlert() {
    alert("This will be removed after first click");
  }

  const button = document.getElementById("removeEventButton");
  button.addEventListener("click", showAlert);

  button.addEventListener("click", function () {
    button.removeEventListener("click", showAlert);
  });
</script>

In this example, an event listener that triggers an alert is added to the button with the ID removeEventButton. Another event listener is added to remove the alert event listener after the first click. Note that removeEventListener requires a reference to the exact same function object used in addEventListener. This is why anonymous functions cannot be removed later—each declaration creates a new, distinct function object.

Info

Leverage event delegation for better performance, especially when working with a large number of child elements.

Event Propagation and Delegation

When an event fires on an element, it does not stop there. By default it travels in two phases: first capturing (top of the DOM down to the target), then bubbling (from the target back up to the root). Most handlers run during the bubbling phase, which is why a click on a button also reaches a click listener on its parent <div>.

This bubbling enables event delegation: instead of attaching a listener to every child, attach one listener to a common parent and read event.target to find out which child was actually clicked.

<ul id="menu">
  <li>Home</li>
  <li>About</li>
  <li>Contact</li>
</ul>

<script>
  // One listener handles clicks on any current OR future <li>
  document.getElementById("menu").addEventListener("click", function (event) {
    if (event.target.tagName === "LI") {
      console.log("You clicked:", event.target.textContent);
    }
  });
</script>

A single listener on <ul> handles every <li>, including ones added to the list later — far more efficient than wiring up each item individually. For a deeper look at the phases and stopPropagation(), see bubbling and capturing.

Best Practices

Use Event Delegation

Add a single event listener to a parent element to manage all child elements' events. This improves performance and reduces the number of event listeners.

Avoid Anonymous Functions for Event Handlers

Using named functions for event handlers makes it easier to remove them later and improves code readability.

Clean Up Event Listeners

Ensure that event listeners are removed when they are no longer needed to avoid memory leaks and improve performance.

Minimize the Number of Event Listeners

Attach event listeners to higher-level elements instead of numerous individual elements to reduce memory usage and improve performance.

Use once Option in addEventListener

Utilize the once option to automatically remove the event listener after it is triggered once, preventing potential memory leaks.

button.addEventListener("click", function handler() {
  console.log("Triggered once");
}, { once: true });

Prevent Default Actions and Stop Propagation Appropriately

Use event.preventDefault() and event.stopPropagation() judiciously to control event behavior without interfering with other handlers.

Debounce or Throttle Event Handlers

Use debouncing or throttling techniques to optimize the performance of event handlers that are triggered frequently, such as scroll or resize events.

Conclusion

Mastering JavaScript events is crucial for creating dynamic and interactive web applications. By understanding how to use events like click, mouseover, and keydown, and how to add and remove event listeners with addEventListener() and removeEventListener(), you can significantly enhance user interactions on your web pages.

Practice

Practice
Which of the following statements about event handling in the DOM are true?
Which of the following statements about event handling in the DOM are true?
Was this page helpful?