For my study project, I’m building a Chat application, similar to Slack. And if you ever used Slack you know there are different channels and if there is no channel that you need for the project, you can always create one.

The problem I faced is that all your channels in DOM working fine on content load, until you add a new channel and DOM is not aware of what happened after content loaded. Let’s look at HTML first:

<div id="channels">
   <div>
      <a href="" class="nav-link active" data-page="First">First</a>
   </div> 
   <div>    
      <a href="" class="nav-link" data-page="Second">Second </a>
   </div>
</div>

And to make it more visual here is my ugly UI (don’t worry I will clean it up once the project is done)

On page load everything looks nice and clean I can switch the channels and load chats with ajax requests.

// Set links up to load new pages
document.querySelectorAll('.nav-link').forEach(link => {
  link.onclick = function() {
    // code goes in here

    // avoid default page load on click, instead we use ajax
    return false;
    };
});

Well, now if you add a channel and DOM already loaded link.onclick would never get triggered because the new element never existed on the first load.

So what to do then? Here is where we get rid of the old approach and start using JavaScript Event Delegation. Instead of querying for individual elements (like we did above), we going to query for parent element and watch what happens when you click on the child:

 const my_channels = document.getElementById("channels");
 my_channels.addEventListener("click", function(e){
    // declare target the element we just clicked
    const target = e.target;
    // avoid default page load on click, instead we use ajax
    e.preventDefault();
    // if clicked element is <a> do something
    if(target.matches("a")){
       //do something here
       // for example log the clicked link text
       console.log(target.innerText);
    };
});

So what happened there? We got the parent element, which in our case is div container with id channels. Then we waiting for any event inside that container, in our case click. Once the event fired we get the target element, simply child inside the parent on which we clicked. And if the target matches our expectation we want something to happen.

Using event delegation in JavaScript helps us work with dynamic DOM elements and it’s not that complicated as you might think at first glance.