Start.dev
Back to blog

JavaScript addEventListener: clicks, inputs and events

Learn addEventListener step by step: handle clicks, typing and form submits, use the event object, and avoid the mistakes that break listeners.

Ariel Weinberger
Ariel Weinberger ยท 6 min read

How do you make a web page react when someone clicks a button or types in a box?

Short answer: find the element, then call addEventListener with the name of the event and a function to run. The browser calls your function every time that event happens.

const button = document.querySelector("#save")
 
button.addEventListener("click", () => {
  console.log("Saved!")
})

By the end of this post you will be able to handle clicks, typing and form submits, read the event object, and spot the mistakes that make a listener silently do nothing. If finding elements with querySelector is new, read What the DOM is first.

How addEventListener works

Every call has the same shape:

element.addEventListener(type, listener)
  • element is the thing you are listening to, like a button, an input, a form, or the whole document.
  • type is the event name as a string: "click", "input", "submit", "keydown".
  • listener is the function to run. You pass the function itself. The browser calls it later, when the event happens.

That last point is the whole idea. You aren't running the code now. You are handing the browser a function and saying "run this when it happens". Reacting To Events (opens in a new tab) explains the model in the course.

Handling a click

Here is a counter. The HTML has a button and a span:

<button id="like">Like</button>
<span id="count">0</span>

The JavaScript finds both, keeps a number, and updates the page on every click:

const likeButton = document.querySelector("#like")
const countEl = document.querySelector("#count")
let likes = 0
 
likeButton.addEventListener("click", () => {
  likes = likes + 1
  countEl.textContent = likes
})

The arrow function runs once per click. likes lives outside it, so the number survives between clicks. That works because of closures, which JavaScript closures explained covers in more depth. Try it in Your First Click Handler (opens in a new tab).

The event object

The browser passes your function one argument: an object describing what just happened. It is usually called event or e.

likeButton.addEventListener("click", (event) => {
  console.log(event.type)   // "click"
  console.log(event.target) // the element that was clicked
})

The properties you will use most:

PropertyWhat it tells youTypical use
event.targetThe element the event started onWhich button in a list was clicked
event.target.valueThe current text of an inputReading what the user typed
event.keyThe key that was pressed, like "Enter"Keyboard shortcuts
event.preventDefault()Stops the browser's default actionKeeping a form from reloading the page

The Event Object (opens in a new tab) goes through each of these.

Reacting to typing: input vs change

Inputs have two events that sound alike but fire at different times.

  • input fires on every keystroke, paste or delete. Use it for live feedback.
  • change fires when the value is committed. For a text box, that is when it loses focus after being edited. For a checkbox or a dropdown, it is right away.

A live character counter uses input:

const bio = document.querySelector("#bio")
const remaining = document.querySelector("#remaining")
 
bio.addEventListener("input", (event) => {
  const left = 160 - event.target.value.length
  remaining.textContent = `${left} characters left`
})

Swap "input" for "change" and the counter only updates when you click away, which feels broken. Input And Change Events (opens in a new tab) has both side by side.

Handling a form submit

Listen for submit on the form, not click on the button. That way pressing Enter in a field works too. By default, submitting a form reloads the page, so call event.preventDefault() first:

const form = document.querySelector("#signup")
 
form.addEventListener("submit", (event) => {
  event.preventDefault()
  const email = form.querySelector("#email").value
  console.log("Signing up", email)
})

Without preventDefault(), the page reloads and your console.log disappears before you can read it. See Preventing Default (opens in a new tab).

Keyboard events

Use keydown and check event.key:

document.addEventListener("keydown", (event) => {
  if (event.key === "Escape") {
    closeMenu()
  }
})

Listening on document catches the key wherever focus is on the page. event.key gives readable names like "Enter", "Escape" and "ArrowDown".

Removing a listener, or running it once

To stop listening, call removeEventListener with the same type and the same function. That means the function needs a name:

function handleClick() {
  console.log("clicked")
}
 
button.addEventListener("click", handleClick)
button.removeEventListener("click", handleClick)

If you only need it once, pass an options object instead:

button.addEventListener("click", handleClick, { once: true })

The browser removes the listener after the first click.

Listening to many items at once

If a list has 50 delete buttons, you don't need 50 listeners. Put one listener on the list and check what was clicked:

const list = document.querySelector("#todos")
 
list.addEventListener("click", (event) => {
  const button = event.target.closest(".delete")
  if (!button) return
  button.closest("li").remove()
})

Clicks bubble up from the button to the list, so the one listener hears them all. It also works for items you add later. This is called event delegation, and Event Delegation (opens in a new tab) covers it fully.

addEventListener vs onclick

You will also see button.onclick = () => { ... }. It works, but it is a single slot: setting it again replaces the old handler. addEventListener can attach as many listeners as you want, supports options like once, and can be removed cleanly. Most modern code uses addEventListener.

Common mistakes

  • Calling the function instead of passing it. button.addEventListener("click", handleClick()) runs handleClick immediately and passes its return value, usually undefined. Write handleClick with no parentheses.
  • Adding on to the event name. The event is "click", not "onclick". The wrong name doesn't throw an error. The listener just never fires.
  • Selecting the element before it exists. If your script runs before the HTML is parsed, querySelector returns null and you get Cannot read properties of null (reading 'addEventListener'). Load the script with defer, or put it at the end of the body. See Adding JavaScript To A Page (opens in a new tab).
  • Using querySelectorAll like a single element. It returns a list. Loop over it with forEach, or use delegation.
  • Trying to remove an anonymous function. removeEventListener needs the exact same function. Two arrow functions that look identical are still different functions.

Quick recap

  • element.addEventListener("click", fn) runs fn every time the event happens.
  • Your function gets an event object. event.target, event.key and event.preventDefault() are the ones to learn first.
  • Use input for live typing, change for committed values, and submit on the form itself.
  • Pass the function, don't call it, and make sure the element exists first.
  • One listener on a parent can handle clicks for every child.

Frequently asked questions

What does addEventListener do in JavaScript?

It tells the browser to run a function whenever a specific event happens on an element. You can attach it to buttons, inputs, forms, the document, or the window.

What is the difference between addEventListener and onclick?

onclick holds one handler, so assigning it again replaces the last one. addEventListener lets you attach several listeners to the same event, pass options, and remove a specific one later.

Why is my event listener not working?

The most common causes are calling the function instead of passing it, using "onclick" as the event name, or selecting the element before it exists on the page. Check the console for a null error first.

What is event.target?

It is the element where the event started. In a list of buttons, event.target tells you which one was clicked.

How do I remove an event listener?

Call removeEventListener with the same event type and the same named function you added. If you only want it to fire once, pass { once: true } when you add it.

Practice

The matching exercises are in the course. Start with Listening For Clicks (opens in a new tab), read live typing in Input And Change Events (opens in a new tab), then put it together in Project Product Filter (opens in a new tab). Each exercise works in your browser, so there is nothing to install.

Keep going

JavaScript DOM (opens in a new tab) takes you from your first click handler to forms, validation and event delegation. If functions and arrays still feel shaky, JavaScript Basics (opens in a new tab) is the right first step.

Level up your coding skills

Jump into interactive courses and start building real-world projects.

Start learning

Related posts

The DOM is the browser's live version of your HTML. Learn how JavaScript finds elements, changes text and classes, reacts to clicks and builds new ones.
Ariel Weinberger
Ariel Weinberger ยท 5 min read
Learn JavaScript closures with tested examples: counters, the var vs let loop question, private state, debounce, and the stale closure bug in React hooks.
Brad Traversy
Brad Traversy ยท 8 min read
Learn the JavaScript array methods you will use daily: map, filter, find, some, every, reduce and sort, with runnable examples on one shopping cart.
Ariel Weinberger
Ariel Weinberger ยท 5 min read