Back to CourseBeginner3 min read
JavaScript /

Event Listeners and Handlers

Make pages react to clicks, typing and form submissions with addEventListener, the event object, bubbling and delegation.

Bishal Biswas

Bishal Biswas

Self Learner

Published on: March 10, 2026
Last updated: September 5, 2026
Beginner Topic

What is an event?

An event is something that happens on the page. A click, a key press, a form submission, the page finishing loading. Your job is to say “when that happens, run this function”.

addEventListener

const button = document.querySelector('#myButton');
button.addEventListener('click', function () {
  console.log('Button was clicked');
});

Three parts: the element, the event name, and the function to run.

The function is called a handler or a callback. It does not run immediately, it waits for the event.

You can add more than one

button.addEventListener('click', function () {
  console.log('First handler');
});
button.addEventListener('click', function () {
  console.log('Second handler');
});

Both run, in the order they were added. This is why addEventListener is better than the old onclick property, which only holds one function:

button.onclick = function () { console.log('A'); };
button.onclick = function () { console.log('B'); };

Only B survives there.

Common events

element.addEventListener('click', handler);
element.addEventListener('dblclick', handler);
element.addEventListener('mouseenter', handler);
element.addEventListener('mouseleave', handler);
input.addEventListener('input', handler);
input.addEventListener('change', handler);
input.addEventListener('focus', handler);
input.addEventListener('blur', handler);
form.addEventListener('submit', handler);
document.addEventListener('keydown', handler);
window.addEventListener('resize', handler);
window.addEventListener('scroll', handler);

input fires on every keystroke, while change fires only when the field loses focus. Use input for live search and change for validation.

The event object

Your handler automatically receives an object describing what happened.

button.addEventListener('click', function (event) {
  console.log(event.type);
  console.log(event.target);
  console.log(event.clientX, event.clientY);
});

Most people call the parameter event or just e.

For keyboard events:

document.addEventListener('keydown', function (event) {
  console.log(event.key);
  if (event.key === 'Escape') {
    console.log('Escape was pressed');
  }
  if (event.ctrlKey && event.key === 's') {
    event.preventDefault();
    console.log('Save shortcut');
  }
});

For input events:

const search = document.querySelector('#search');
search.addEventListener('input', function (event) {
  console.log('Typing:', event.target.value);
});

preventDefault

Some elements have built in behaviour. A form reloads the page when submitted, a link navigates away. preventDefault() stops that.

const form = document.querySelector('#myForm');
form.addEventListener('submit', function (event) {
  event.preventDefault();
  const name = document.querySelector('#name').value;
  console.log('Submitted name:', name);
});

Without preventDefault, the page reloads and your JavaScript never gets to do anything. This is the most common reason a form handler “does not work”.

Bubbling

When you click a button inside a div, the click fires on the button, then the div, then the body, then the document. The event travels up the tree. This is called bubbling.

document.querySelector('#outer').addEventListener('click', function () {
  console.log('Outer div');
});
document.querySelector('#inner').addEventListener('click', function () {
  console.log('Inner button');
});

Clicking the button prints Inner button then Outer div.

To stop it:

button.addEventListener('click', function (event) {
  event.stopPropagation();
  console.log('This does not reach the parent');
});

Use stopPropagation sparingly. It can break other code that legitimately listens higher up.

Event delegation

Bubbling is not a nuisance, it is a feature. Instead of adding a listener to every item, add one to the parent.

const list = document.querySelector('#todoList');
list.addEventListener('click', function (event) {
  if (event.target.matches('.delete-btn')) {
    event.target.closest('li').remove();
  }
});

This has two big advantages:

  • One listener instead of a hundred, so less memory.
  • It works for new items added later, which individual listeners would miss.

event.target is what was actually clicked. event.currentTarget is the element the listener sits on.

Removing a listener

You need a named function, because the reference must match.

function handleClick() {
  console.log('Clicked');
}
button.addEventListener('click', handleClick);
button.removeEventListener('click', handleClick);

For a listener that should fire only once, there is a shortcut:

button.addEventListener('click', function () {
  console.log('This runs only the first time');
}, { once: true });

Quick recap

  • addEventListener(type, handler) connects an event to a function.
  • You can attach several handlers to the same event.
  • The event object carries details like target, key and mouse position.
  • preventDefault() stops built in behaviour such as a form reloading the page.
  • Events bubble up the tree, which makes event delegation possible.
  • Delegation means one listener on a parent instead of many on children.
  • Use { once: true } for a handler that should fire a single time.
script.js

// The basic pattern: element, event name, handler
const button = document.querySelector('#myButton');
button?.addEventListener('click', function (event) {
  console.log('Clicked!', event.type);
  console.log('Element clicked:', event.target.tagName);
});
// Stop a form from reloading the page
const form = document.querySelector('#myForm');
form?.addEventListener('submit', function (event) {
  event.preventDefault();                       // essential
  const name = document.querySelector('#name').value.trim();
  if (!name) {
    console.log('Please enter a name');
    return;
  }
  console.log('Submitted:', name);
});
// Live typing with the input event
const search = document.querySelector('#search');
search?.addEventListener('input', function (event) {
  console.log('Searching for:', event.target.value);
});
// Keyboard shortcuts
document.addEventListener('keydown', function (event) {
  if (event.key === 'Escape') console.log('Escape pressed');
  if (event.ctrlKey && event.key === 's') {
    event.preventDefault();
    console.log('Save shortcut');
  }
});
// Event delegation: one listener handles every item, even new ones
const list = document.querySelector('#todoList');
list?.addEventListener('click', function (event) {
  if (event.target.matches('.delete-btn')) {
    event.target.closest('li')?.remove();
    console.log('Item deleted');
  }
});
// Fire a handler only once
button?.addEventListener('click', function () {
  console.log('Welcome, this shows only the first time');
}, { once: true });

Was this explanation helpful?

Your feedback helps us improve the content for everyone.