Search code examples
javascriptloopsevent-listeneriife

addEventListener on loop not working with IIFE


In the code below, the value of j is always set to the last of the iteration. I'm already using an IIFE, but it still gives the non-expected behavior. Is there something that I'm doing wrong?

function renderUsers(users) {
    const frag = document.createDocumentFragment();
    const userList = document.getElementById('user-list');

    // Create and append all the users on the user list
    for (var j = 0; j < users.length; j++) {
        var item = document.createElement('li');
        var division = document.createElement('div');
        var userName = document.createElement('span');
        var deleteButtonAnchor = document.createElement('a');
        var deleteButton = document.createElement('i');
        deleteButton.classList.add('material-icons');
        deleteButton.textContent = 'delete_forever';
        (function() {
            deleteButton.addEventListener('click',function() {
                console.log(j);
            });
        })();
        deleteButtonAnchor.appendChild(deleteButton);
        division.appendChild(userName);
        division.appendChild(deleteButtonAnchor);
        item.appendChild(division);


        userName.appendChild(document.createTextNode(users[j].name.first+' '+users[j].name.last));
        frag.appendChild(item);
    }
    userList.appendChild(frag);
}

Solution

  • What you want to do with closure, is to pass the iterating variable as a parameter, as such:

     (function(j) {
         deleteButton.addEventListener('click',function() {
              console.log(j); // j is the parameter
         });
     })(j);
    

    Or as @torazaburo fairly noticed in the comments, you can use let keyword for a iterating variable to eliminate the need to create closures. More on the let keyword you can find here. Note however, that it is ES6 feature, which is not implemented in older browsers, so you may need to transpile it first (for example using babel).