JavaScript Event Listeners
The coat room has one peg. The button is #peg, and the word on it is hang. Under it, #ticket says blank. Nothing is listening yet. peg.click() leaves the ticket blank, and the button still says hang.
addEventListener stores a function on the button. That line does not run the function. The ticket stays blank until peg.click() runs whoever is listening. This page can hold more than one function. Add wool, then add silk. Silk does not replace wool. One click runs both, wool first, then silk. The ticket becomes wool silk.
The previous page used onclick. That property holds one function, so a second assignment replaces the first. This page does not assign onclick. Passing the same wool function to addEventListener a second time adds nothing. One click runs that function once. removeEventListener drops the function you pass, the same one you added. A new function with the same lines is a different object, so wool stays. The page script adds both and clicks once. The line under the peg reads wool silk / 2 / peg. This page does not create an element, does not set a CSS property, and does not read the event object.
Two hooks, not one slot
The previous page put one function in bell.onclick. A second assignment erased the first, and the line was dong / 1 / bell. This page does not read onclick, typeof, or this. getElementById only hands you the peg button and the ticket. The new idea is addEventListener: each call can add another listener, and one click runs the list in order. The event argument is the next page. These functions take no parameter.
Who is listening
The button starts with hang on it and blank under it. Read the rows in order. Each snippet starts from that fresh button, except the page row, which is the demo at the bottom. The page demo does not call removeEventListener.
📊 Who is listening
| Moment | Who is listening | Ticket text | Easy to mix up |
|---|---|---|---|
click, no listener yet |
nobody | blank | a bare click writing wool |
addEventListener wool, before click |
wool | blank | the add call writing wool |
click with only wool |
wool | wool | the button text becoming wool |
wool then silk, one click |
both, wool then silk | wool silk | only the second running |
removeEventListener wool, then click |
silk | silk | both still running |
page script, one click |
both | wool silk | the line ending with hang |
addEventListener("click", wool) appends wool to the button’s click list. It does not call wool. peg.click() fires a click and runs the list from first to last. The call does not change the button’s own text. A second addEventListener("click", silk) appends silk after wool. Both run. Order is the order you added them.
The same function object is not added twice. peg.addEventListener("click", wool) and then that same call again leaves a list of one. One click runs wool once, parts.length is 1, and the ticket is wool.
removeEventListener("click", wool) removes that object. Silk, if you added it, stays. A brand-new function does not match, even when its body also pushes "wool". After peg.removeEventListener("click", function () { parts.push("wool"); }) the original wool is still on the list. A click still writes wool. join(" ") only puts a space between the words already in parts. An empty list is never joined on this page, because the ticket starts as the word blank and changes only inside a listener.
Nobody is listening
The file opens with the button and the paragraph. No listener has been added. The click runs, and these two logs ask the ticket and the word on the button.
let peg = document.getElementById("peg");
let ticket = document.getElementById("ticket");
peg.click();
console.log(ticket.textContent);
console.log(peg.textContent);
The logs are blank and hang. The click had an empty list, so the paragraph did not change.
The listener waits
addEventListener stores wool. The body does not run on that line. parts stays empty, the ticket stays blank, and the button still says hang.
let peg = document.getElementById("peg");
let ticket = document.getElementById("ticket");
let parts = [];
function wool() {
parts.push("wool");
ticket.textContent = parts.join(" ");
}
peg.addEventListener("click", wool);
console.log(ticket.textContent);
console.log(parts.length);
console.log(peg.textContent);
The logs are blank, 0, and hang. Wool is on the list. Nothing has called it, so parts has no word yet.
click runs whoever is on the list
This snippet starts from the fresh button again. Only wool is added, then peg.click() runs it. The ticket becomes wool. The button text stays hang. parts.length is 1.
let peg = document.getElementById("peg");
let ticket = document.getElementById("ticket");
let parts = [];
function wool() {
parts.push("wool");
ticket.textContent = parts.join(" ");
}
peg.addEventListener("click", wool);
peg.click();
console.log(ticket.textContent);
console.log(peg.textContent);
console.log(parts.length);
The logs are wool, hang, and 1. One listener, one word. The word hang is still the button.
Silk sits beside wool
Fresh button again. wool is added, then silk, and only then comes one click. There is no click between the two adds. Wool pushes "wool" and writes it. Silk pushes "silk" and writes both words with a space. The ticket is wool silk. parts.length is 2. The button stays hang.
let peg = document.getElementById("peg");
let ticket = document.getElementById("ticket");
let parts = [];
function wool() {
parts.push("wool");
ticket.textContent = parts.join(" ");
}
function silk() {
parts.push("silk");
ticket.textContent = parts.join(" ");
}
peg.addEventListener("click", wool);
peg.addEventListener("click", silk);
peg.click();
console.log(ticket.textContent);
console.log(parts.length);
console.log(peg.textContent);
The logs are wool silk, 2, and hang. Silk did not erase wool. One click ran the list in add order.
Take wool off the peg
Fresh button. Both functions are added, then removeEventListener is called with the same wool function. Silk stays. One click runs silk only. The ticket is silk, not wool silk. parts.length is 1.
let peg = document.getElementById("peg");
let ticket = document.getElementById("ticket");
let parts = [];
function wool() {
parts.push("wool");
ticket.textContent = parts.join(" ");
}
function silk() {
parts.push("silk");
ticket.textContent = parts.join(" ");
}
peg.addEventListener("click", wool);
peg.addEventListener("click", silk);
peg.removeEventListener("click", wool);
peg.click();
console.log(ticket.textContent);
console.log(parts.length);
console.log(peg.textContent);
The logs are silk, 1, and hang. Wool was the same function object you added, so it left the list. The page demo below does not remove anyone.
One line under the peg
The script sits at the end of the body, after the button and the paragraphs exist. It adds wool, then silk, then calls click once. Each function adds 1 to hits and pushes its word. The line joins the ticket, the hit count, and peg.id. It does not write the button text. It does not read an event argument.
<!DOCTYPE html>
<html>
<head>
<title>event-listeners — page</title>
</head>
<body>
<button id="peg" type="button">hang</button>
<p id="ticket">blank</p>
<p id="line">…</p>
<script>
let peg = document.getElementById("peg");
let ticket = document.getElementById("ticket");
let parts = [];
let hits = 0;
function wool() {
hits = hits + 1;
parts.push("wool");
ticket.textContent = parts.join(" ");
}
function silk() {
hits = hits + 1;
parts.push("silk");
ticket.textContent = parts.join(" ");
}
peg.addEventListener("click", wool);
peg.addEventListener("click", silk);
peg.click();
document.getElementById("line").textContent =
ticket.textContent + " / " + hits + " / " + peg.id;
</script>
</body>
</html>
The line reads wool silk / 2 / peg. The ticket is wool silk, not blank. The count is 2, because both listeners ran on one click. The last token is the button id, peg.id, not the word hang. The button still says hang. The next page is the event object. It reads the argument these functions skip.
Five checks at the peg
- Click with no listener. The ticket is blank and the button is hang.
- Add wool and do not click. The ticket is blank,
parts.lengthis 0, and the button is hang. - Add wool, then click. The ticket is wool, the button is hang, and
parts.lengthis 1. - Add wool, add silk, then click once. The ticket is wool silk,
parts.lengthis 2, and the button is hang. - Add both, remove wool, then click. The ticket is silk,
parts.lengthis 1, and the button is hang. Read the page line: wool silk / 2 / peg.
Where this peg tricks you
The button still says hang. The list held both functions, or it held only silk, or it held nothing.
- Expecting the second
addEventListenerto replace the first, the wayonclickdid - Expecting
addEventListeneritself to change the ticket to wool - Expecting only silk to run when both were added
- Expecting the button text to become wool
- Expecting
removeEventListenerwith a new function to drop wool - Adding the same
woolfunction twice and expecting two runs - Reading
onclick,typeof null, orthis.idhere; the doorbell page did that - Reading
event.targethere; these functions take no argument, and the event object is the next page - Building the button with
createElement; an earlier page did that - Reading the line as blank / 0 / hang
What stays with you
- With no listener,
peg.click()leaves the ticket blank and the button as hang addEventListenerleaves the ticket blank untilclick- One click with only wool sets the ticket to wool and leaves the button as hang
- Wool then silk, then one click, sets the ticket to wool silk
removeEventListenerwith the same wool, then click, sets the ticket to silk- The same wool added twice runs once, and a new function does not remove it
- The page line reads wool silk / 2 / peg
🧠 Test Your Knowledge
Test Your Knowledge
Challenge yourself with this interactive quiz and see how well you understand the topic
📝 Instructions
- Read each question carefully
- Select the best answer for each question
- You can retake the quiz as many times as you want
- Your progress will be shown at the top