donnikitos
donnikitos

Reputation: 986

closures not working in firefox sdk

I am trying to create a button list with automatically assigned onclick events.

document.getElementById('buttonList').innerHTML = '';
for(var $c = 0; $c < $buttons.length; $c++) {
    document.getElementById('buttonList').innerHTML += '<li><button id="button-' + $c + '">' + $buttons[$c].text + '</button></li>';
    document.querySelector('#button-' + $c).onclick = (function($index) {
        return function() {
            console.log($index);
        };
    })($c);
}

In my opinion, if i click a button it should log 0/1/2/3/... But none of the buttons is working, except the last one. The last button returns the right index.

So my question, what is wrong?

Upvotes: 1

Views: 106

Answers (2)

user228534
user228534

Reputation:

When you use innerHTML += ..., it destroys the older onclick handlers. You can fix this in one of two ways:

  • Use appendChild instead of innerHTML += ...:

    document.getElementById('buttonList').innerHTML = '';
    for(var $c = 0; $c < $buttons.length; $c++) {
        var li = document.createElement('li');
        var button = document.createElement('button');
        button.setAttribute('id', 'button-' + $c);
        button.innerHTML = $buttons[$c].text;
        button.onclick = (function($index) {
            return function() {
                console.log($index);
            };
        })($c);
        li.appendChild(button);
        document.getElementById('buttonList').appendChild(li);
    }
    
  • Generate the HTML first, then add the event handlers all at once:

    document.getElementById('buttonList').innerHTML = '';
    for(var $c = 0; $c < $buttons.length; $c++) {
        document.getElementById('buttonList').innerHTML += '<li><button id="button-' + $c + '">' + $buttons[$c].text + '</button></li>';
    }
    
    for(var $c = 0; $c < $buttons.length; $c++) {
        document.querySelector('#button-' + $c).onclick = (function($index) {
            return function() {
                console.log($index);
            };
        })($c);
    }
    

Upvotes: 2

Artyom Neustroev
Artyom Neustroev

Reputation: 8715

When you modify the innerHTML property, the element gets rerendered, so the elements, that you've bind the onclick to, do not exist in the DOM anymore (except the last one).

From MDN:

Removes all of element's children, parses the content string and assigns the resulting nodes as children of the element.

If you still want to use innerHTML, the solution is to create a container element (<li>), append it to your list and then modify its innerHTML:

document.getElementById('buttonList').innerHTML = '';
for (var $c = 0; $c < $buttons.length; $c++) {
    var liItem = document.createElement("li");
    document.getElementById('buttonList').appendChild(liItem);
    liItem.innerHTML = '<button id="button-' + $c + '">' + $buttons[$c].text + '</button>';
    console.log(document.querySelector('#button-' + $c));
    document.querySelector('#button-' + $c).onclick = (function ($index) {
        return function () {
            console.log($index);
        };
    })($c);
}

Fiddle example

Upvotes: 2

Related Questions