Responding to Clicks with addEventListener

An event is something that happens on the page, such as a click, and addEventListener tells the browser which of your functions to run each time it happens.

Apprentice · JavaScript · Web Development · Updated

After this lesson you can

  • Run a function when an element is clicked, with addEventListener and a named or an arrow function
  • Pass a function to addEventListener without calling it, and stop listening with removeEventListener

Key takeaways

  • An event is something that happens on the page, such as a click, a key press or typing in a box. An event listener is the function that runs when it happens.
  • element.addEventListener('click', showSaved) tells the browser to run showSaved every time that element is clicked.
  • Hand over the function by its name, with no parentheses. showSaved runs later, on each click; showSaved() runs once, right away, and the clicks do nothing.
  • removeEventListener stops listening. It needs the same event name and the same named function that addEventListener was given.

Tap the heart on a recipe and it turns red. Click Dinner and only the dinner recipes stay on the screen. Press Show steps and the steps slide open. Every one of those starts with the same line of JavaScript: "when this button is clicked, run this function". You'll write that line whenever a page has to answer a person, from a class project's quiz button to a dark-mode switch to an Add to cart button.

This chapter builds the JavaScript for Weeknight Recipes, a small recipe site for busy evenings: filter buttons by meal, a "show steps" toggle on every recipe card, and a favorite heart. This lesson starts with one button and one click.

Events: the page's doorbell

You don't stand at the front door all evening waiting for a friend. You get on with your evening, and when the doorbell rings, you go and answer it.

A web page works the same way. Your JavaScript sets things up when the page loads, then waits. When something happens, the browser rings a bell: it announces an event. An event is anything that happens on the page that your code can answer, and each kind has a name:

Event name When it happens
click Someone clicks or taps an element, or presses Enter on a focused button
keydown Someone presses a key on the keyboard
input The text in a box changes, one letter at a time
submit A form is sent

The function that answers is called an event listener (you'll also see event handler: the same idea). It "listens" for one kind of event on one element, and runs each time that event happens there.

addEventListener: "when this happens, run that"

You attach a listener to an element with its addEventListener method. It's read the way it's spelled: "add event listener".

element.addEventListener('click', functionToRun);
  • element is the element to listen to, found with querySelector, as in Locating DOM elements.
  • 'click' is the event's name, as a string.
  • functionToRun is the function the browser runs each time that event happens on that element.

A function you hand to other code, for it to call later, is a callback, the same idea you met in Functions as Values: Callbacks. Here the browser is the one that calls it.

A first listener: the save button

A recipe page has a button and an empty paragraph. This is the part of the page's HTML that matters here:

<button id="save-recipe">♡ Save recipe</button>
<p id="saved-message"></p>

The JavaScript finds both, writes a function that fills in the message, and hands that function to the button:

const saveButton = document.querySelector('#save-recipe');
const savedMessage = document.querySelector('#saved-message');

function showSaved() {
  savedMessage.textContent = 'Saved to your weeknight list.';
}

saveButton.addEventListener('click', showSaved);

When the page loads, all four statements run, and nothing changes on the screen: the paragraph is still empty. The last line only tells the browser "when this button is clicked, run showSaved". Click the button and the message appears. Click it again and showSaved runs again.

Try it. In your course folder, make a folder named weeknight, with this index.html in it. It's the whole file:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Weeknight Recipes</title>
</head>
<body>
  <h1>Weeknight Recipes</h1>
  <button id="save-recipe">♡ Save recipe</button>
  <p id="saved-message"></p>
  <script type="module" src="app.js"></script>
</body>
</html>

Put the JavaScript above in app.js beside it, open the page with Live Preview, and click the button. Keep this page: the next lessons add to it.

An arrow function works too

A listener doesn't need a name. For a short job that belongs to one button, an arrow function (=>, read "arrow") written right inside the call is common:

const cookedButton = document.querySelector('#cooked');
let timesCooked = 0;

cookedButton.addEventListener('click', () => {
  timesCooked += 1;
  cookedButton.textContent = `Cooked ${timesCooked} times`;
});

After three clicks, the button says Cooked 3 times. The variable timesCooked lives outside the arrow function, so it keeps its value from one click to the next.

Which to choose? A named function when you'll use it in more than one place, or remove it later (below). An arrow function for a short listener that only that one button uses.

Hand over the function; don't call it

When you go out and leave a babysitter the recipe for dinner, you hand them the recipe card. You don't cook the meal yourself on your way out the door.

showSaved, with no parentheses, is the recipe card: the function itself, for the browser to run later. showSaved(), with parentheses, means "run it now". This line has one pair of parentheses too many:

saveButton.addEventListener('click', showSaved());

It's one of the most common bugs in browser JavaScript, and it gives no error message. Here's what happens:

  1. showSaved() runs straight away, as the page loads, so the message is there before anyone clicks.
  2. showSaved gives back nothing, which is undefined, and undefined is what gets handed to addEventListener.
  3. There's no function to run, so every click does nothing.

When the function needs a value, wrap the call in an arrow function. The arrow function is what gets handed over, and the call inside it waits for the click: saveButton.addEventListener('click', () => showMessage('Saved!'));

Stop listening with removeEventListener

Weeknight Recipes lets each visitor vote for a recipe once. After the first click, the button should stop counting. removeEventListener takes the listener off again, and it's given the same two things as addEventListener:

const voteButton = document.querySelector('#vote');
const votes = document.querySelector('#votes');

function addVote() {
  votes.textContent = Number(votes.textContent) + 1;
  voteButton.textContent = 'Thanks for voting!';
  voteButton.removeEventListener('click', addVote);
}

voteButton.addEventListener('click', addVote);

With 41 votes on the page, three clicks give 42, not 44: the first click counts the vote and removes the listener, so the next two clicks have nothing to run.

The function must be the very same one. Two arrow functions that look alike are still two different functions, like two copies of the same recipe card, so this removes nothing:

voteButton.addEventListener('click', () => console.log('vote'));
voteButton.removeEventListener('click', () => console.log('vote'));

Every click still prints vote. To remove a listener later, give it a name.

In older code you'll also see a listener written into the HTML, as <button onclick="showSaved()">. It works, but it mixes JavaScript into the HTML and allows one listener per event, so this course uses addEventListener.

Check yourself

2 questions to try on the lesson page.

So what

Find an element, hand it a function with addEventListener, and the page answers back: every like button, menu and game control you've ever clicked is built from that one line.

Key terms

Cheat sheets

Related