Question

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 anevent 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>';
Was it helpful?

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();
        }
    });

OTHER TIPS

Its better to attach the onclick handler before appending to the DOM element. This should work

<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 a = document.createElement("a");
    a.href='#';
    a.id = "user-" + user.id ;
    a.onclick = function(event){
         console.log(this);
         event.preventDefault();
     }
    a.innerHTML = user.id;
    document.getElementById("list").appendChild(a);

}
</script>

Try the following at this fiddle:

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

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.createElement("a");
    element.id = "user-" + user.id;
    element.innerHTML = "user-" + user.id;

    document.getElementById("list").appendChild(element);
    console.log(element);

    element.onclick = function(event){
        console.log(this);
        event.preventDefault();
    };
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top