Adding eventListeners to checkboxes in for loop in chrome-extension

562 views Asked by At

I have a function that gets all the tabs in a window and then updates the popup.html page by adding a checkbox for each tab. After doing this there is a second for loop that adds event listeners which update an dictionary containing the value of the checkbox's. The problem is that any checkbox I click only updates the last element in the dictionary.

Here is the code:

function viewTabs() {
  var queryInfo = {lastFocusedWindow: true};
  // Go through all the tabs open, and add them to the scrollview along with a number and checkbox
  chrome.tabs.query(queryInfo, function(tabs) {
    document.getElementById("openLinks").innerHTML = "";
    (function() {
      for (i = 0; i < tabs.length; i++) {
        // add checkboxes to each link for the user to include or disclude
        document.getElementById("openLinks").innerHTML += 
            "<li><input type=\"checkbox\" id = \"" + i.toString() + "\" checked>" + 
            tabs[i].title + "</li>";
        checkedTabsArr[i.toString()] = true;
      }
      for (i = 0; i < tabs.length; i++) {
        document.getElementById(i.toString()).addEventListener("click",
          function() {
            console.log(checkedTabsArr);
            checkedTabsArr[i.toString()] = !checkedTabsArr[i.toString()];
        });
      }
    }());
  });
}

Here is the output (I am clicking different checkboxes not just 6):

Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true}
Buttons.js:174 Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true}
Buttons.js:174 Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: false}
Buttons.js:174 Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true}
Buttons.js:174 Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: false}
Buttons.js:174 Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true}
Buttons.js:174 Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: false}
Buttons.js:174 Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: true}
Buttons.js:174 Object {0: true, 1: true, 2: true, 3: true, 4: true, 5: true, 6: true, 7: false}

I have tried with and without closures.

1

There are 1 answers

3
JRad the Bad On BEST ANSWER

Why are you using two separate for loops inside the same context?

EDIT I've tracked this down to an issue with how javascript handles both click and change events with checkboxes.

Note: Don't ever use click events for checkboxes, use onChange instead. Also, don't assign onchange using javascript. It fails spectacularly for some reason that I can't identify.

Instead, use jQuery for it. Make sure you have the jQuery library included on your page and load your script after all DOM elements are loaded on the page. Then this code should work for you just fine:

function viewTabs() {
    var queryInfo = {
        lastFocusedWindow: true
    };

    // Go through all the tabs open, and add them to the scrollview along with a number and checkbox
    chrome.tabs.query(queryInfo, function (tabs) {

        for (i = 0; i < tabs.length; i++) {
            var thisTab = tabs[i];

            // add checkboxes to each link for the user to include or disclude
            // start by creating a blank checkbox element
            var thisCheckbox = document.createElement('input');
            thisCheckbox.type = "checkbox";
            thisCheckbox.id = i;
            thisCheckbox.checked = true;

            //Add the event listener to the newly created checkbox
            attachChangeListener(thisCheckbox, i);
            console.log(thisCheckbox);

            // create a blank <li>
            var thisListItem = document.createElement('li');

            // add the checkbox and tab title to <li>
            thisListItem.appendChild(thisCheckbox);
            thisListItem.appendChild(document.createTextNode(thisTab.title));

            // add <li> to the end of the openLinks element and add a new value to the array
            document.getElementById("openLinks").appendChild(thisListItem);
            checkedTabsArr[i] = true;

            console.log(tabs[i].title + ": " + thisCheckbox.checked);

        }

        function toggle(checkbox, title) {
            console.log("CLICK EVENT FOR: " + title);

            //toggle the state of the checkbox
            checkbox.checked = !checkbox.checked;
            console.log(title + " updated: " + checkbox.checked);


        }

        function attachChangeListener(element, index) {
            $(element).change(function () {
                if ($(this).is(":checked")) {
                    //match the array's value to that of the new checkbox state
                    checkedTabsArr[index] = true;
                    console.log(checkedTabsArr);
                } else {
                    //match the array's value to that of the new checkbox state
                    checkedTabsArr[index] = false;
                    console.log(checkedTabsArr);
                }
            });
        }
    });

The above code should hopefully be a plug and play solution for you.

Let me know how this goes for you.

Fiddle: https://jsfiddle.net/wof8ebq7/27/