Search code examples
javascriptdom-events

JavaScript add events to newly created elements in a loop


I have a loop that creates Html links and assign click events to them in the same loop. The click event seems to work only for the last element that the loop creates.

<div id="list">
</div>

<script type="text/javascript">
    
    users = { 
        1 : { id : 1 },
        2 : { id : 2 },
        3 : { id : 3 },
        4 : { id : 4 }              
    };
    
    for (var index in users) {
        
        var user = users[index];
        document.getElementById("list").innerHTML += '<a href="" id="user-' + user.id + '">' + user.id + '</a>';
         
         var element = document.querySelector("#user-" + user.id);
         console.log(element);
         
         element.addEventListener("click", function(event){
             console.log(this);
             event.preventDefault();
         });
         
    }
</script>

Here only the last link is click-able. However if the links have already been created then all the event listeners will work.

<div id="list">
   <a href="#" id="user-1">1</a>        
   <a href="#" id="user-2">2</a>        
   <a href="#" id="user-3">3</a>        
   <a href="#" id="user-4">4</a> 
</div>


<script type="text/javascript">
    
    users = { 
        1 : { id : 1 },
        2 : { id : 2 },
        3 : { id : 3 },
        4 : { id : 4 }              
    };
    
    for (var index in users) {
        
         var user = users[index];
         var element = document.querySelector("#user-" + user.id);
         console.log(element);
         
         element.addEventListener("click", function(event){
             console.log(this);
             event.preventDefault();
         });
         
    }
</script>

This seems to work without any issues. Is there away to make sure that an event is attached to the element at creation?

Note: I don't want to use JS framework for this. I know that this can be done easily using jQuery.

$( "body" ).on('click', "#user-" + user.id ,function(){});

Note: The code I posted is a simple code I created on the fly to illustrate the problem. I am using a template engine that return a string, that's why I am using

document.getElementById("list").innerHTML += '<a href="" id="user-' + user.id + '">' + user.id + '</a>';

Solution

  • Your problem is in .innerHTML which breaks the DOM with every iteration, which means the previously added events are wiped out and you only have the listener attached to the very last element. You can use DOM API to create and append these elements and everything will work fine: jsfiddle.net/HtxZb

    I would, however, recommend using event delegation. Without jQuery you can implement it like this:

    document.getElementById('list')
        .addEventListener('click', function(event){ 
            var elem = event.target;
            console.log( elem );
            if( elem.nodeName.toLowerCase() === "a" ){ // a link was clicked
                /* do something with link
                   for example 
                    switch( elem.id ){
                    }
                */
                event.preventDefault();
            }
        });