JS | Understanding Event Bubbling / by Joseph Palumbo

I’ve seen a lot of #100DaysOfCode posts on both Twitter and Instagram, which got me thinking about doing a similar challenge. I don’t really have the time to commit to coding a minimum of an hour a day, at least not for personal projects, so I decided to modify the challenge by trying to create 100 different websites this year, something made famous by Jennifer Dewalt a while back.

It’s been a while since I did any serious (or even not-so-serious) web development/design, so I refreshed my skills this weekend with this tutorial by James Quick on how to build a quiz app using HTML, CSS, and (plain) Javascript. It’s a great example of what you can do with the basic building blocks of the Internet and I highly recommend it.

A Simple Task Tracker…

The idea for my first site was to build a very simple task tracker.

…but a complex problem!

When the user adds a new task into the input field and clicks the “Add Action Item” button, the JavaScript pulls the string from the field and adds it to an empty unordered list as a new list item element with the class of incomplete.

Like this…

<div>
  <ul id="task-list">
    <li class="action-item incomplete">Something to do</li>
  </ul>
</div>

A click on the action item triggers an event which changes the class from incomplete to completed which adds some CSS to it

Here’s the CSS:

.completed {
    color: red;
    text-decoration: line-through; 
}

And here’s the JavaScript:

// marks task complete
$("#action-item").removeClass('incomplete').addClass('completed');

The problem I ran into was that since the element was created after the page was loaded it wasn’t responding to the normal event trigger. After some Googling I learned that what I was doing was called direct binding, which will only attach to an element that exists at the time my code makes a binding call.

According to the jQuery documentation, nothing happens when you click a newly added item because of the directly event handler was previously attached. Direct events are only attached to elements at the time a method is called. In this case, since my new list items did not exist when the jQuery was initialized at the time of page load, it does not get the handler.

Event Bubbling

The answer is to use Event Delegation which is how you handle events when you’re creating elements dynamically.

To use event delegation, you first have to understand how events propagate, or bubble, throughout and up the DOM. In essence, any time an anchor tag (i.e. <a>) is clicked, an event fires that bubbles up the DOM tree triggering each of the parent click event handlers, as in the diagram below.

Clicking the &lt;a&gt; element will fire an event that travels up all of the parent elements until it get’s to the root document.

Clicking the <a> element will fire an event that travels up all of the parent elements until it get’s to the root document.

This means that any time you click one of the bound anchor tags, you’re effectively clicking the entire document. And because of this we can create a delegated event that will catch a click on an element dynamically created after the DOM loads.

Here’s how the new function in my site looks like after learning about this.

$('#task-list').on('click', '.action-item', function() {
  $(this).removeClass('incomplete').addClass('completed'); 
}); 

Notice how in the new JS I’ve moved the .action-item selector to the second parameter position of the .on() method which tells the handler to listen for the specified event (click), and when it hears it, check to see if it matches the second parameter. .

This is a brief description and explanation of Event Delegation in JavaScript. For more in depth information on how to handle these types of events, please go read the appropriate documentation in the links I’ve provided through out the poset.