Loading...

JavaScript Event Delegation

The ticket window has one strip. The parent is #strip. Two buttons are already on it: #red, text red, and #blue, text blue. Under the strip, #note says blank.
Event delegation means one listener on a parent handles clicks that happen on its children. You call addEventListener on the strip, not on each button. When a click lands on a button, the click travels up to the strip. event.target is the button. event.currentTarget is the strip, the element this listener sits on.
The gold ticket is not in the markup yet. The page creates #gold after the listener is stored, then appends it and clicks it. The same function still runs, because the listener sits on the parent. The note becomes gold. The page script clicks red, then blue, then gold. addEventListener ran once, so the count hands is 1. The line under the strip reads red / blue / gold / 1. These buttons are type="button". This page does not call preventDefault or stopPropagation. The plaque page did that, and it did not add a child after the listener.

One listener for every ticket

The plaque page named event and read type, target, and currentTarget on a link inside a wall. That line was click / seal / plaque / true / quiet. The coat peg stored wool and silk with no parameter. That line was wool silk / 2 / peg. This page keeps the parameter event and uses target for a different job: telling which child was clicked, including one that did not exist yet.
getElementById only hands you the strip, the red button, the blue button, and the note. Gold is born from createElement. A listener on red plus a listener on blue would miss gold unless you attached again after appendChild. One listener on the strip does not need that second attachment. Read currentTarget inside the function. After the function returns, that property is null.

What the strip records

The note starts as blank. The button labels stay red, blue, and later gold. Read the rows in order. Each snippet starts from the two-button strip, except the page row, which is the demo at the bottom. The page demo calls addEventListener once.

📊 What the strip records

Moment What you read Note text Easy to mix up
listener on the strip, click red event.target.id red reading currentTarget, which is strip
same click, read currentTarget event.currentTarget.id strip reading the ticket id
same listener, click blue event.target.id blue giving blue its own listener
gold created after the listener, then clicked event.target.id gold thinking a new button is silent
click the strip itself, guard on the note skip writing the id strip
page script the line red / blue / gold / 1 the plaque line, or a last token of 3

A click on a button is one event. target stays that button for every listener along the trip. This page has one listener, on the strip, so currentTarget is the strip while the function runs. The click reaches the strip because nothing calls stopPropagation. The plaque page cut that trip. This page does not.
preventDefault is for a browser default, such as a link leaving the page. These controls are buttons, not links. Skipping preventDefault is the whole story here, not a missing line.
If event.target and event.currentTarget are the same node, the click landed on the strip, not on a button. The handler writes skip and does not pretend the paper is a ticket. seen.join(" / ") only joins ids already pushed. hands is written after that join, so the last token is how many times the page called addEventListener, not how many tickets were clicked.

The ticket under the click

The listener sits on the strip. The function names the parameter event and writes event.target.id into the note. Then the page clicks red. It does not call addEventListener on red.

Read the ticket

javascript
let strip = document.getElementById("strip");
let note = document.getElementById("note");
function take(event) {
note.textContent = event.target.id;
}
strip.addEventListener("click", take);
document.getElementById("red").click();
console.log(note.textContent);

The log is red. The listener’s element is the strip. target reports the button under the click, not the parent.

Who owns the listener

Same fresh strip, same listener, same click on red. The note writes both ids, target first, then currentTarget.

Read both ids

javascript
let strip = document.getElementById("strip");
let note = document.getElementById("note");
function take(event) {
note.textContent = event.target.id + " " + event.currentTarget.id;
}
strip.addEventListener("click", take);
document.getElementById("red").click();
console.log(note.textContent);

The log is red strip. The first word is the ticket. The second word is the element that owns this listener. A listener placed on red itself, then a click on red, would log red red. This reading uses the strip, so the two ids differ.

The ticket already beside it

Fresh strip again. One listener, still only on the strip. This time the click is blue. Blue was in the markup from the start. It still has no listener of its own.

Hear the other ticket

javascript
let strip = document.getElementById("strip");
let note = document.getElementById("note");
function take(event) {
note.textContent = event.target.id;
}
strip.addEventListener("click", take);
document.getElementById("blue").click();
console.log(note.textContent);

The log is blue. One addEventListener covered both buttons that were already inside the strip. Blue did not replace red, and red did not need a second call.

A ticket printed later

The listener is stored first. Then the page builds a button, sets its id to gold, sets type to button, sets the text to gold, and appends it to the strip. Only then does it click gold.

Hear a ticket added later

javascript
let strip = document.getElementById("strip");
let note = document.getElementById("note");
function take(event) {
note.textContent = event.target.id;
}
strip.addEventListener("click", take);
let gold = document.createElement("button");
gold.id = "gold";
gold.type = "button";
gold.textContent = "gold";
strip.appendChild(gold);
gold.click();
console.log(note.textContent);

The log is gold. Gold did not exist when addEventListener ran. The click still travels from gold up to the strip, and target is gold. You do not attach a listener to gold.

A click on the paper

The strip is also a clickable node. If the click’s target is the strip, there is no ticket id to keep. The function compares target with currentTarget. When they match, the note becomes skip.

Ignore a click on the strip

javascript
let strip = document.getElementById("strip");
let note = document.getElementById("note");
function take(event) {
if (event.target === event.currentTarget) {
note.textContent = "skip";
return;
}
note.textContent = event.target.id;
}
strip.addEventListener("click", take);
strip.click();
console.log(note.textContent);

The log is skip. Both target and currentTarget are the strip, so the guard refuses the paper. A click on red still writes red, because those two references differ. Leave the guard out and strip.click() would write the id strip into the note.

The line under the strip

The script sits at the end of the body, after the strip and the paragraphs exist. One listener is stored, and hands becomes 1. Red is clicked, then blue. Each ticket id is pushed into seen. Gold is created, appended, and clicked, so gold is pushed too. The line is written after that third click. The page never clicks the strip itself, so skip is not in the line.

Page demo — ticket strip

html
<!DOCTYPE html>
<html>
<head>
<title>event-delegation — page</title>
</head>
<body>
<div id="strip">
<button id="red" type="button">red</button>
<button id="blue" type="button">blue</button>
</div>
<p id="note">blank</p>
<p id="line">…</p>
<script>
let strip = document.getElementById("strip");
let note = document.getElementById("note");
let seen = [];
let hands = 0;
function take(event) {
if (event.target === event.currentTarget) {
note.textContent = "skip";
return;
}
note.textContent = event.target.id;
seen.push(event.target.id);
}
strip.addEventListener("click", take);
hands = hands + 1;
document.getElementById("red").click();
document.getElementById("blue").click();
let gold = document.createElement("button");
gold.id = "gold";
gold.type = "button";
gold.textContent = "gold";
strip.appendChild(gold);
gold.click();
document.getElementById("line").textContent =
seen.join(" / ") + " / " + hands;
</script>
</body>
</html>

The line reads red / blue / gold / 1. The three ids are the tickets that were clicked, in order. The last token is hands, and addEventListener ran once, so that token is 1, not 3. The note ends as gold, because gold was the last click. The word blank is gone. The word skip never appears. The plaque line and the peg line are other pages.
A button that only holds text keeps event.target on the button, so id is red, blue, or gold. Put a span inside the button and the click can land on the span. The span has no id. This page does not do that.

At the window

  1. Put one listener on the strip and click red. The note is red. Red has no listener of its own.
  2. With that same listener, click blue. The note is blue.
  3. Create gold, append it, and click it. The note is gold. The listener was already on the strip.
  4. Click the strip itself. With the guard, the note is skip. Then read the page line: red / blue / gold / 1.

Misses at the window

The labels still say red, blue, and gold. The note is not the word ticket.

  • A listener on red and another on blue, then surprise that gold stays silent
  • Reading event.currentTarget when you wanted the ticket
  • Reading event.target when you wanted the strip
  • Forgetting the guard, so strip.click() writes strip
  • A span inside the button, so target is the span and the id is empty
  • Calling addEventListener again on gold, then counting hands as 2
  • Calling preventDefault or stopPropagation; the plaque page did that
  • Expecting the handler argument to be the button element
  • Reading the line as click / seal / plaque / true / quiet, or as wool silk / 2 / peg
  • Reading the last token as 3 because three tickets were clicked

Keep this

  • One addEventListener on the strip hears every button inside it
  • On a click of red, event.target.id is red and event.currentTarget.id is strip
  • The same listener writes blue when blue is clicked
  • Gold, created after the listener, still writes gold
  • A click on the strip itself, with the guard, writes skip
  • The page line reads red / blue / gold / 1

🧠 Test Your Knowledge

Ready to Start

Test Your Knowledge

Challenge yourself with this interactive quiz and see how well you understand the topic

❓
8
Questions
🎯
70%
To Pass
♾️
∞
Time
🔄
∞
Attempts

📝 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