DevAcademy
LearnJavaScriptDOM Events
IntermediateJavaScript

DOM Events

Learn how to listen for user interactions with addEventListener(), what information the event object carries, the most common event types, and how bubbling and capturing work.

Reading Time

16 min

Lesson

Lesson 34 of 48

What is an Event?

An event is a signal that something has happened — the user clicked a button, typed into a field, submitted a form, or the page finished loading. The browser fires these events continuously as the user interacts with the page, and JavaScript can "listen" for specific ones and run code in response. This is the mechanism that makes web pages interactive rather than static documents: without events, a button click would just be a click, with nothing listening or reacting to it.

addEventListener()

addEventListener() is the standard way to react to events. It's called on the element you care about, takes the event type as a string (like "click") and a callback function to run when that event fires. It's preferred over the older approaches — an inline onclick="..." attribute in the HTML, or assigning a function directly to element.onclick — for a simple but important reason: addEventListener() lets you attach multiple listeners for the same event on the same element without one overwriting another. Assigning to .onclick, by contrast, replaces whatever handler was there before, since there can only ever be one.

addEventListener vs. the onclick Property

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

The Event Object

Every listener callback automatically receives an event object as its first argument, carrying details about what just happened. event.type tells you the event's name (like "click"), event.target is the actual element the event originated from (the element the user clicked, for instance), and event.currentTarget is the element the listener is attached to — these two can differ when the event bubbles up from a descendant. event.preventDefault() stops the browser's default behavior for that event (like a link navigating, or a form submitting), and event.stopPropagation() stops the event from continuing to bubble up to ancestor elements.

target, currentTarget, preventDefault, stopPropagation

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Common Event Types

EventFires When...
clickAn element is clicked
inputThe value of an <input>, <textarea>, or <select> changes, as it happens
changeA form control loses focus (or is toggled) after its value changed
submitA <form> is submitted
keydownA key is pressed down
keyupA key is released
mouseover / mouseoutThe pointer enters or leaves an element
focus / blurAn element gains or loses keyboard focus
loadA resource (page, image, script) finishes loading

input vs. change, and keydown

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Removing Listeners with removeEventListener()

removeEventListener() detaches a previously attached listener, but only if you pass it the exact same function reference that was originally added — an anonymous inline function can never be removed, because there's no way to refer back to it afterward. This is the main reason to write listener callbacks as named functions (or store the function in a variable) whenever you might need to remove them later, such as when a component is unmounted or a temporary interaction ends.

A Removable Listener Needs a Named Reference

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Bubbling and Capturing, Briefly

When an event happens on a nested element, it doesn't just fire on that element — it travels through the DOM tree in two phases. The capturing phase runs first, traveling from the document down to the target element; the bubbling phase runs second, traveling back up from the target through each of its ancestors. By default, addEventListener() listens during the bubbling phase; passing true (or { capture: true }) as a third argument listens during the capturing phase instead. This matters because a click on a deeply nested <span> will also trigger click listeners on its parent, grandparent, and so on, all the way up — a behavior the next lesson on event delegation puts to direct, practical use.

A Click Bubbling Up Through Ancestors

Try it yourself — edit and run

Console Output

Click “Run” to see the console output here.

Key Takeaways

  • addEventListener() supports multiple listeners per event and is safer than assigning .onclick directly
  • The event object carries target, currentTarget, type, and the methods preventDefault() and stopPropagation()
  • input fires on every keystroke; change fires once, after the value is committed
  • removeEventListener() requires the exact same function reference used in addEventListener()
  • Events bubble upward through ancestors by default; capturing runs the other direction, first

Arrow Functions Can't Be Removed Easily

Every time an inline arrow function or anonymous function expression is evaluated, it creates a brand-new function value, even if the code looks identical to one written elsewhere. Passing one directly to addEventListener() means you have no reference to pass to removeEventListener() later. If a listener may ever need to be removed, assign the function to a named variable or declare it with function name() { ... } first.

Use preventDefault() Deliberately

Calling event.preventDefault() on a submit event is extremely common when handling forms with JavaScript (to send data via fetch instead of a full page reload), but it's easy to overuse. Only call it when you genuinely intend to override the browser's default behavior — calling it unnecessarily on things like links or checkboxes can make a page feel broken to users who expect standard behavior.

Interview Questions

Quick Quiz

1. What is a key advantage of addEventListener() over setting element.onclick directly?

2. Which event object method stops the browser from performing its default action, such as a form submitting?

3. Which event fires on every keystroke inside a text input?

4. What is required in order for removeEventListener() to successfully remove a listener?

5. By default, during which phase does a listener added with addEventListener() run?