Make a page react to clicks and key presses with addEventListener.
Why this matters
A page that only changes once, when it loads, is not very interactive. Real interfaces wait for the user to do something (click a button, type in a box, press Enter) and then respond. The browser announces each of these actions as an event, and your code can listen for it. Every button, form, menu and game control on the web works this way.
addEventListener
You attach a listener to an element with addEventListener(type, handler):
<button id="like">Like</button>
<span id="count">0</span>
const likeButton = document.querySelector("#like");
const countEl = document.querySelector("#count");
let likes = 0;
function handleLike() {
likes++;
countEl.textContent = likes;
}
likeButton.addEventListener("click", handleLike);
- The first argument is the event type as a string:
"click","input","keydown","submit","change"... - The second is a function the browser will call each time the event happens.
Notice handleLike has no parentheses. You are handing the function to the browser to call later. Writing handleLike() would call it once, immediately, and pass its return value (undefined) as the listener. This is the most common event bug.
Short handlers are often written inline as arrow functions:
likeButton.addEventListener("click", () => {
likes++;
countEl.textContent = likes;
});
The event object
The browser passes an event object to your handler with details about what happened:
input.addEventListener("keydown", (event) => {
console.log(event.key); // "a", "Enter", "ArrowUp", ...
console.log(event.target); // the element the event happened on
});
event.key tells you which key was pressed, so you can react to Enter only:
const nameInput = document.querySelector("#name");
const greeting = document.querySelector("#greeting");
nameInput.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
greeting.textContent = `Hello, ${nameInput.value}!`;
}
});
An input's current text is in its value property, and it is always a string.
Common event types
| event | fires when |
|---|---|
click | an element is clicked or tapped (also Enter/Space on a focused button) |
input | the value of a text box changes, on every keystroke |
change | a select, checkbox or text box is committed |
keydown | a key is pressed |
submit | a form is submitted |
Prefer click on real <button> elements over clicks on <div>s. A button can be reached with the keyboard and announced by screen readers; a clickable div cannot, unless you rebuild all of that yourself.
preventDefault
Some events have a default action: clicking a link navigates, and submitting a form reloads the page. event.preventDefault() cancels that so your code can handle it instead:
form.addEventListener("submit", (event) => {
event.preventDefault(); // stay on this page
// ...read the inputs and update the page
});
Event delegation
If you have many similar elements, or elements that are added later (like to-do items), you can put one listener on their parent and check event.target:
list.addEventListener("click", (event) => {
if (event.target.matches("button.delete")) {
event.target.closest("li").remove();
}
});
This works because most events bubble: a click on a button inside an li inside a ul is also a click on the li and the ul. You will use this in the next lesson.
Removing a listener
removeEventListener needs the same function you added, which is one reason to give handlers names:
likeButton.removeEventListener("click", handleLike);
Common mistakes
- Calling the handler instead of passing it.
addEventListener("click", handle())is wrong; usehandle. - Misspelling the event type.
"onclick"and"Click"silently do nothing. The type is"click". - Adding the listener inside a loop or another handler by accident, so it runs twice per click.
- Forgetting that
valueis a string."2" + 1is"21"; convert withNumber(). - Clickable divs. Use
<button>for anything that acts like a button.
Quick recap
element.addEventListener("type", handler)runshandlerevery time the event fires.- Pass the function, do not call it.
- The event object tells you what happened:
event.key,event.target. preventDefault()stops default actions; delegation handles many children with one listener.
Try it
Open the Do tab: make a Like button that counts clicks, and a name box that greets the user when they press Enter.