JavaScript Event Delegation: Handle Dynamic Course Buttons
- Get link
- X
- Other Apps
Practical Web Development — Lesson 5 of 10 · Intermediate
When a catalogue adds new rows after loading, attaching a separate click handler to each initial button can leave the new buttons inactive. Event delegation places one handler on a stable parent and identifies which descendant initiated the event. This example uses real buttons, so keyboard activation keeps its normal browser behaviour.
Create the list and one parent listener
<ul id="catalogue">
<li>HTML <button type="button" data-course="html"><span>Shortlist</span></button></li>
</ul>
<button id="add" type="button">Add CSS row</button>
<p id="status" role="status"></p>
<script>
const catalogue = document.querySelector('#catalogue');
const selected = new Set();
catalogue.addEventListener('click', event => {
if (!(event.target instanceof Element)) return;
const button = event.target.closest('button[data-course]');
if (!button || !catalogue.contains(button)) return;
const id = button.dataset.course;
if (!['html', 'css'].includes(id)) return;
selected.add(id);
document.querySelector('#status').textContent =
'Shortlisted: ' + [...selected].join(', ');
});
document.querySelector('#add').addEventListener('click', event => {
const row = document.createElement('li');
row.append('CSS ');
const button = document.createElement('button');
button.type = 'button'; button.dataset.course = 'css';
button.textContent = 'Shortlist';
row.append(button); catalogue.append(row);
event.currentTarget.disabled = true;
});
</script>Understand target and currentTarget
The target may be the span inside the button. A check that requires the target itself to be a button would miss that click. The closest call walks up to the matching button. The containment check ensures that the matched element belongs to this delegated region. In the parent handler, currentTarget is the list that owns the listener.
Click events bubble through ancestors, which makes this arrangement possible. Not every event behaves the same way. For example, focus and blur do not bubble in the same manner as click; choose focusin or a deliberate capture strategy when the behaviour calls for it. Do not add stopPropagation indiscriminately, because other features may rely on events reaching a parent.
Keep rendering separate from authority
The data attribute identifies a UI choice. It does not prove the user can enrol in a course. The short allowlist in the demo keeps the example predictable; real authorisation belongs at the server. We construct nodes and use textContent rather than inserting user-controlled HTML strings.
Verify the dynamic case
- Click the text inside the HTML button; it should shortlist HTML.
- Add the CSS row, then use its new button without registering another list listener.
- Use Tab and Enter to perform the same actions.
- Click empty space in the list; nothing should change.
- Shortlist a course twice; the Set should prevent duplicate entries.
Practice
Add a remove button with a separate data-action value. Validate the action and course ID before changing state. Explain which element should own the handler if the whole catalogue is replaced: delegation only survives while its parent listener remains attached to an existing node.
Continue learning
Review our semantic HTML project, JavaScript course filter and course platform architecture.
Reference: Technical documentation for this lesson. Examples are learning exercises; test them in your environment.
- Get link
- X
- Other Apps
Comments
Post a Comment