loading...
Cover image for Adding Event Listeners to the future DOM elements using Event Bubbling

Adding Event Listeners to the future DOM elements using Event Bubbling

akhil_001 profile image Akhil sai ・3 min read

Brief Intro :

Before I get into details, let me answer the primary questions so that you need not waste time reading the whole article

  • Who is this for?
    • Any native javascript developer who has beginner knowledge of DOM API
  • What will I learn?
    • You will be learning how to handle the use cases where you have to add an event listener to an element that might be added to UI in the future workflow. In short, you will be learning Event Delegation

Problem Statement :

It all started when I was developing a simple MVC to-do app using vanilla javascript. I came across a requirement of listening to the click event of the task item that is going to be added to the UI when the user clicks on 'Add Task Button'.

Then I started to wonder, how can one add the event listeners to the elements that will be added in the future workflow?

Generally, one can add an event listener to an element that already exists in the DOM in the following way

 let taskDOMElement = document.querySelector('#task');
 taskDOMElement.addEventListener('click',clickHandler,false);
 function clickHandler()
 {
    //handle the event
 }

But in order to add the event listeners to the elements that are going to be appended to DOM in the future, we have to use the concept of Event Bubbling where the events bubble from the target Element to the parent Element invoking their respective event handlers

Solution :

  • First, we have to figure out the nearest non-dynamic element in our DOM that will not be changed in our workflow(in my case it is body element)
let rootElement = document.querySelector('body');
  • Then attach the same event listener to that element you want to listen from the future element(in my case, it is click event)
let rootElement = document.querySelector('body');
rootElement.addEventListener('click',function(event){},true);
  • Now check if the event target matches your selector(in my case its a li tag)
let rootElement = document.querySelector('body');
rootElement.addEventListener('click',function(event){
let targetElement = event.target
let selector = 'li';
if(targetElement.match(selector) {
//logic for handling the click event of li tag
 }
},true);
  • But to be more precise , we have to traverse from the target to the root Element for capturing the event
rootElement.addEventListener('click',function(event){
let targetElement = event.target
let selector = 'li';
while(targetElement != null) {
if(targetElement.match(selector) {
   //logic for handling the click event of li tag
   return;
  }
  targetElement = targetElement.parentElement;
 }
},true);
  • So after few touchups,here is the generalised function to handle the event listeners of future elements
addCustomEventListener: function (selector, event, handler) {
        let rootElement = document.querySelector('body');
        //since the root element is set to be body for our current dealings
        rootElement.addEventListener(event, function (evt) {
                var targetElement = evt.target;
                while (targetElement != null) {
                    if (targetElement.matches(selector)) {
                        handler(evt);
                        return;
                    }
                    targetElement = targetElement.parentElement;
                }
            },
            true
        );
    }

//adding the Event Listeners to all the li tasks
addCustomEventListener('li#task','click',taskClickHandler);

  • So, this is how you can attach event listeners to the future elements i.e., elements that are added dynamically in future

Links

Credits

Cover Image by Alejandro Alvarez

Conclusion:

I hope you find this article useful and helpful at some point in time. I would love to hear your feedback on the article and discuss more on this

Posted on by:

akhil_001 profile

Akhil sai

@akhil_001

Coder,Comics Lover, Mythology Enthusiast

Discussion

pic
Editor guide
 

Hi. Thank you for taking the trouble to post this solution. However, I am finding your example somewhat difficult to follow. It would have been very helpful to see a worked example showing the html code and the related eventhandler code so that I could understand exactly what you are working with and translate your example for my website layout. I have never used vanilla JS, coming to javascript via reactJS which is, of course, quite different in many respects...
Also, I found it difficult to determine which terms in your code are javascript terms and which need to be replaced by me with element names, IDs or classes. Again, a fully-worked example would have been very useful here.

I haven't seen the notation 'li#task' before and I and if it is to be replaced by something meaningful in my environment, I am not sure what to replace it with? My event function name?

Also, in the while loop, I am not really sure why you are navigating up the parent elements
In my case, I have a navbar object (flex-container) with individual tiles with id: "linkContainer" and it is to these tiles that I want to add the event handlers, but they are all siblings. I think, anyway, I need to be navigating down if I am starting at the root element. If I start at the root I need to find the flex container first (which is a child of the root element I imagine, and then I will need to find the linkContainer children elements of the flex container. Again, without being able to see the HTML structure you are working with, it is hard to envisage what is happening here.

With regard to the statement (targetElement.matches(linkcontainer)) { It is not clear whether I add a text string with the element.id, as I have done, or just the ID name without quotes? Again a fully-worked example would make that clear.

This is my HTML structure. If you have some suggestions which would help me apply your code to this I would be very thankful.


Home
About
Technology
Marketing
Support
Modules

It is the linkcontainer Tile1 ...Tile6 elements above which require the event handlers.

ReactDOM.render(


,
document.getElementById('root')

Cheers from sunny Sydney

Rapier

 

Ok, for reactJS users a workable solution is:

var rootElement = document.getElementById('root');
console.log(rootElement);
rootElement.addEventListener('click', rootElementClicked);

console.log('event listener added to root element');

function rootElementClicked(event) {
  event.preventDefault();
  const { name, value } = event.target;

  console.log("Root element clicked with [" + event.target.className, event.target.id);
}

So, the event-listener is app-wide, so a click anywhere will call the event-handler function. Then in the code for that function, the element class and ID will tell you what was clicked.

Rapier...

Note the event.preventDefault(); line - it prevents a refresh of the web page, otherwise the target class & ID are returned as "undefined"