The DOM & EventsIntermediate8 min10 / 12

Events & Listeners

Learn how to make your web pages respond to user actions by wiring up event listeners, working with the event object, and understanding how events travel through the DOM.

A static web page is like a brochure — it looks nice, but it doesn't react to you. The moment you want a button to do something when clicked, a form to validate before submitting, or a search box to filter results as you type, you need events. Events are the heartbeat of interactive web development. In this lesson you will learn how to listen for user actions and run your own code in response — this is where JavaScript truly comes alive.

#What Is an Event?

Think of it like

The Doorbell Analogy

Think of your browser as a house. When someone presses the doorbell, a signal fires. If you have set up a listener (you are standing near the door), you hear it and react — open the door, peek through the window, or ignore it. If no one is listening, the doorbell ring disappears into silence. An event is the doorbell press; an event listener is you, deciding what to do when it rings.

The browser fires events constantly — when the user clicks, moves the mouse, presses a key, submits a form, or even just scrolls. Your job is to register a listener on a specific element so your function runs when that event occurs.

#addEventListener — The Core API

addEventListener takes two required arguments: the event name (a string like "click") and a callback function that runs when the event fires. You call it on any DOM element.

The callback runs every time the button is clicked.
const button = document.querySelector('#myButton');

button.addEventListener('click', function () {
  console.log('Button was clicked!');
});

Arrow functions work just as well and are often shorter:

Named or arrow — both work. You can even attach multiple listeners to the same element.
button.addEventListener('click', () => {
  console.log('Arrow function handler fires too!');
});

#Common Event Types

There are dozens of event types. Here are the ones you will reach for most often as a beginner:

| Event name | When it fires | |---|---| | click | User clicks an element | | input | Value of an <input> or <textarea> changes | | change | Input loses focus after its value changed | | submit | A <form> is submitted | | keydown | A key is pressed | | mouseover | Mouse moves onto an element |

#The Event Object

Every time an event fires the browser automatically passes an event object to your callback. This object is packed with useful information: which key was pressed, which element was clicked, where the mouse was, and much more. You just name a parameter in your callback to receive it — by convention it is called e or event.

e.target is the element the user actually interacted with.
button.addEventListener('click', (e) => {
  console.log('Event type:', e.type);
  console.log('Clicked element:', e.target);
  console.log('Mouse X position:', e.clientX);
});

For input events, e.target.value gives you the current text the user has typed — incredibly useful for live search or validation:

Fires on every keystroke, giving you real-time feedback.
const searchBox = document.querySelector('#search');

searchBox.addEventListener('input', (e) => {
  console.log('User typed:', e.target.value);
});

#event.preventDefault — Stopping Default Browser Behaviour

Some HTML elements have built-in behaviour. Clicking a link navigates the page. Submitting a form reloads the page. Sometimes you want to intercept that action and handle it yourself with JavaScript. That is exactly what event.preventDefault() does.

Without preventDefault() the page would reload and you'd lose the values.
const form = document.querySelector('#signupForm');

form.addEventListener('submit', (e) => {
  e.preventDefault(); // Stop the page from reloading

  const name = document.querySelector('#nameInput').value;
  if (name.trim() === '') {
    console.log('Name cannot be empty!');
  } else {
    console.log('Form submitted with name:', name);
  }
});
Common mistake

Forgetting preventDefault on Forms

This is one of the most common beginner mistakes. If you add a submit listener but forget e.preventDefault(), the browser will reload the page the instant the form is submitted — before your JavaScript has a chance to do anything useful. Always call it first thing inside a submit handler.

#Event Bubbling — How Events Travel Upward

Think of it like

Bubbles Rising in Water

Imagine the DOM as a glass of water, with deeply nested elements at the bottom and document at the top. When an event fires on an element, it is like a bubble released at the bottom — it rises up through every ancestor element all the way to the top. Each ancestor that has a listener for that event will also have its handler called.

Bubbling means a click on a <button> inside a <div> will trigger click listeners on the button AND on the div AND on the body AND on the document. This is usually fine, but it can cause surprising double-triggers when you have listeners on nested elements.

Clicking the button fires inner first, then the event bubbles up to outer.
// HTML: <div id="outer"><button id="inner">Click</button></div>

const outer = document.querySelector('#outer');
const inner = document.querySelector('#inner');

outer.addEventListener('click', () => console.log('Outer div fired!'));
inner.addEventListener('click', () => console.log('Inner button fired!'));

If you want to stop an event from bubbling further, call e.stopPropagation() inside your handler. Use this sparingly — it can make code harder to reason about — but it is good to know it exists.

Quick check

What does event.preventDefault() do when called inside a form's submit listener?

Tip

Removing a Listener When You No Longer Need It

You can remove a listener with element.removeEventListener('click', handlerFn). This requires a named function reference — anonymous arrow functions cannot be removed because there is no variable pointing at them. Name your handler if you ever need to clean it up (useful in single-page apps to avoid memory leaks).

Key takeaways

  • Use addEventListener(eventName, callback) to respond to user actions on any DOM element.
  • The event object (e) is automatically passed to your callback and contains details like e.target, e.type, and e.target.value.
  • Call e.preventDefault() to stop the browser's built-in behaviour, such as a form reload on submit.
  • Events bubble up through ancestor elements by default — a click on a child also triggers listeners on its parents.
  • Common event types you will use daily: click, input, submit, keydown, and change.
Practice challenges
Test yourself · earn XP
0/4
Predict the output#1

The lesson shows that events bubble up from the clicked element to its ancestors. If the user clicks the inner button, what gets logged?

predict-output
// HTML: <div id="outer"><button id="inner">Click</button></div>

const outer = document.querySelector('#outer');
const inner = document.querySelector('#inner');

outer.addEventListener('click', () => console.log('Outer div fired!'));
inner.addEventListener('click', () => console.log('Inner button fired!'));
Fix the bug#2

This code has a bug — what's wrong?

fix-bug
const form = document.querySelector('#signupForm');

form.addEventListener('submit', (e) => {
  const name = document.querySelector('#nameInput').value;
  console.log('Form submitted with name:', name);
});
Fill in the blank#3

Complete this live search box. On every keystroke it should log exactly what the user has typed so far. Fill in the event name and the property that holds the input's current text.

const searchBox = document.querySelector('#search');

searchBox.addEventListener('', (e) => {
  console.log('User typed:', e.target.);
});
Reorder the lines#4

Put the lines in order to build a form handler that stops the page reloading, reads the typed name, and logs it. The submit listener should be fully assembled in the correct sequence.

1
const form = document.querySelector('#signupForm');
2
});
3
  const name = document.querySelector('#nameInput').value;
4
  e.preventDefault();
5
  console.log('Form submitted with name:', name);
6
form.addEventListener('submit', (e) => {
Your turn
Practice exercise

Build a tiny live character counter. There is a textarea with id 'bio' and a paragraph with id 'count'. As the user types in the textarea, update the paragraph to show how many characters have been typed (e.g. '42 characters'). Also wire up a Reset button with id 'resetBtn' that clears the textarea and resets the counter to '0 characters' when clicked.

Try it live — edit the code and hit Run to see the output:

solution.js · editable