← Back tothis vs that

standard event handler vs event delegation

Written byPhuoc Nguyen
23 Aug, 2023
There are two ways to handle events for a given element: standard event handling and event delegation.
With standard event handling, we attach an event listener directly to the element like this:
const addButton = document.getElementById('addButton');
addButton.addEventListner('click', function(e) {
// `e` represents the event instance
It's similar to creating a dedicated function to handle the event.
const handleAddItem = (e) => {
// Triggered when users click the `Add` button
// ...

addButton.addEventListener('click', handleAddItem);
Since each element in your application has its own set of event handlers, it's likely that you have many handlers for different elements.
const editButton = document.getElementById('editButton');

const handleEditItem = (e) => {
// Triggered when users click the `Edit` button

editButton.addEventListener('click', handleEditItem);
As your application logic grows, you may wonder if it's possible to have a single entry point to handle the `click` event of different elements. The good news is that event delegation has got you covered. This approach is simple but effective: instead of having many handlers, we can handle the `click` event of the parent element, which in this case is `document`.
const handleDocumentClick = (e) => {
// ...

document.addEventListener('click', handleDocumentClick);
The `handleDocumentClick` function runs every time a user clicks on any element on the page, including the `Add` and `Edit` buttons.
To check whether one of these buttons was clicked, there are several ways to do it, such as checking the `id` attribute of the event target.
const handleDocumentClick = (e) => {
const id = e.target.getAttribute('id');
if (!id) {
switch (id) {
case 'addButton':
// Users click the `Add` button ...
case 'editButton':
// Users click the `Edit` button ...
Event delegation brings multiple benefits to your application:
  • It saves memory because it avoids creating many handler functions.
  • It makes code more readable and easier to maintain since handlers are managed in a single place.
  • Most importantly, it allows you to handle events for elements that don't exist yet. You can write the event handler first and dynamically create the element later.
The simple example above demonstrates the potential of the event delegation technique. However, let's explore a more advanced example to truly understand its power.

Advanced example

Let's say we're managing a list of tasks that can be added or removed. But before we get into the nitty-gritty, let's take a look at the final demonstration.
To keep things simple, each task has two basic properties: a label and an ID. The ID is generated automatically and has to be unique so we can tell tasks apart.
At first, our example only has two elements: one for entering new tasks and one for displaying the list of tasks.
When users hit the Enter key in the input, we generate a new ID and add the new task to the list.
Each task element has three parts:
  • A checkbox to mark the task as completed
  • The task label
  • A button to remove the task from the list
const newTaskInput = document.getElementById('newTask');

newTaskInput.addEventListener('keydown', (e) => {
// Check if users press Enter and value isn't empty
// ...

// Generate a new id
const newId = id++;

// Add new task
const li = document.createElement('li');

const label = document.createElement('label');

const checkboxEle = document.createElement('input');
checkboxEle.setAttribute('type', 'checkbox');

const taskLabel = document.createTextNode(value);

const removeButton = document.createElement('button');
removeButton.innerHTML = '[remove]';
IDs generation
This code snippet uses a straightforward method to generate incremental IDs. However, there may be other approaches you can consider. Take a look at the alternatives mentioned as follows:
Nothing fancy here. The handler creates various parts and adds them to the newly created task element.
But wait, there's more! Imagine that we need to handle the following events:
  • When users click on the checkbox, mark the task as completed.
  • When users click on the Remove button, remove the task entirely.
The simplest approach is to use the standard event handler.
checkboxEle.addEventListener('change', (e) => {
removeButton.addEventListener('click', (e) => {
However, if users keep creating new tasks, more and more event handlers will be created, which can use up memory and slow things down. To avoid this, we can handle events at the root tasks element.
const tasksEle = document.getElementById('tasks');

tasksEle.addEventListener('change', (e) => {

tasksEle.addEventListener('click', (e) => {
And the best part? We can effortlessly determine which task is selected by adding a specific attribute to each task item:
checkboxEle.setAttribute('data-id', newId);
removeButton.setAttribute('data-id', newId);
And then requesting it from the handler:
tasksEle.addEventListener('change', (e) => {
const id = e.target.getAttribute('data-id');

tasksEle.addEventListener('click', (e) => {
const id = e.target.getAttribute('data-id');

Questions? 🙋

Do you have any questions? Not just about this specific post, but about any topic in front-end development that you'd like to learn more about? If so, feel free to send me a message on Twitter or send me an email. You can find them at the bottom of this page.
I have a long list of upcoming posts, but your questions or ideas for the next one will be my top priority. Let's learn together! Sharing knowledge is the best way to grow 🥷.

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn