Search code examples
javascriptloopsecmascript-6callbacknodelist

Convert for loop to ES6 for of loop


I would like to convert this piece of code to ES-6. So the for loop in the first example I used needs to be a for of loop. What happens is in the second version that the percentage calculation works just as expected as i can see in the console but it doesn't show in the UI. The older, first method works fine. How come?

// This one works fine
displayPercentages: function(percentages) {

          var fields = 
document.querySelectorAll(DOMstrings.expensesPercLabel);

          var nodeListForEach = function(list, callback) {   
            for(var i = 0; i < list.length ; i++) {
                console.log(list[i], i)
                callback(list[i], i)          
            }
        }; 

             nodeListForEach(fields, function(el, index){
              if(percentages[index] > 0){
                 el.textContent = percentages[index] + '%'
             }else{
                el.textContent = '---';
             }

          });
      },


 // Second version has a problem showing percentages in the UI

  displayPercentages: function(percentages) {       

  var fields = document.querySelectorAll(DOMstrings.expensesPercLabel);

  var nodeListForEach = function(list, callback) {
  for(let [el, index] of list.entries()) {
      console.log(el, index)
      callback(el, index)
    }
 }; 

 nodeListForEach(fields, function(el, index){
  if(percentages[index] > 0){
      el.textContent = percentages[index] + '%'
  }else{
      el.textContent = '---';
  }

 });
},

Solution

  • Since your for loop's body uses the index, you're probably best off sticking with a for loop, not switching to for-of.

    You can switch to for-of (on browsers that have implemented making NodeList iterable, or if you polyfill it, or by using Array.from) by spreading the NodeList out into an array (or using Array.from to create an array) and then using Array.prototype.entries, which gives you an an iterator where each value iterated is an [index, value] array:

    displayPercentages: function(percentages) {
      var fields = document.querySelectorAll(DOMstrings.expensesPercLabel);
      for (const [index, el] of [...fields].entries()) {
        if (percentages[index] > 0) {
          el.textContent = percentages[index] + '%'
        } else {
          el.textContent = '---';
        }
      }
    }
    

    (Note that Array.prototype.entries is fairly new and may need polyfilling.)

    But: That's really quite indirect compared to just using a for loop:

    displayPercentages: function(percentages) {
      var fields = document.querySelectorAll(DOMstrings.expensesPercLabel);
      for (let index = 0; index < fields.length; ++index) {
        const el = fields[index];
        if (percentages[index] > 0) {
          el.textContent = percentages[index] + '%'
        } else {
          el.textContent = '---';
        }
      }
    }
    

    ...or for that matter, using the forEach that is on NodeList now (which, again, you can polyfill):

    displayPercentages: function(percentages) {
      document.querySelectorAll(DOMstrings.expensesPercLabel).forEach((el, index) => {
        if (percentages[index] > 0) {
          el.textContent = percentages[index] + '%'
        } else {
          el.textContent = '---';
        }
      });
    }