0

The code below works as expected.

var mySelector = document.querySelectorAll('.mySelector');
var myFunction = function() {
  for (var i = 0; i < mySelector.length; i++) {
    mySelector[i].classList.add('myClass');
  }
}

However, this code generates an error: "Uncaught TypeError: Cannot read property 'classList' of undefined"

var mySelector = document.querySelectorAll('.mySelector');
var myFunction = function() {
  for (var i = 0; i < mySelector.length; i++) {
    setTimeout(function(i){
      mySelector[i].classList.add('myClass');
    }, 1000);
  }
}

I'm sure there is a very simple explanation why that is, but I don't know it.

Why?

UPDATE: new code with parameter removed from setTimeout function

var mySelector = document.querySelectorAll('.mySelector');
var myFunction = function() {
  for (var i = 0; i < mySelector.length; i++) {
    setTimeout(function(){
      mySelector[i].classList.add('myClass');
    }, 1000);
  }
}

UPDATE 2:

Barmar suggested this was an exact duplicate of another question: JavaScript closure inside loops – simple practical example

I grant that it is generically similar and the answer Barmar linked to could probably help someone with more JavaScript experience over the hump I'm facing. But I think my question is different enough to remain open on its own terms for the following reasons: (1) My case is simpler and a correct answer is likely to benefit less skilled JavaScript practitioners such as myself, (2) I'm specifically concerned with setTimeout as it relates to for loops. Again, I'll grant that the answer Barmar referenced could be helpful for many, but it's not particularly helpful for me personally.

Community
  • 1
  • 1
Donkey Shame
  • 754
  • 3
  • 18
  • Sorry, I deleted my answer since it was a shot in the dark. It's been awhile since I've written Javascript. – Carcigenicate May 06 '17 at 13:55
  • Thing you trying to do could be solved by simple recursion. Create function that would have timeout (wrapped in condition comparing iterator and length of array) at end of it where you call that function again after fixed period of time. For example: var mySelector = document.querySelectorAll('.mySelector'); function reLoop(i){ console.log(mySelector[i]); if(i < mySelector.length) setTimeout(function(){ reLoop(++i); }, 1000); } reLoop(0); – Tominator Apr 15 '21 at 10:25

2 Answers2

0

There is a simple situation with js loops. Cause js executes your code async, variable i is more then valid index of mySelector. You can use let i instead of var i, or use js closures. Here you can find more info about this.

Community
  • 1
  • 1
M Melnikov
  • 83
  • 3
0

As Carcigenicate said, you don't have access to i. The following will just print undefined.

var mySelector = document.querySelectorAll('.mySelector');
var myFunction = function() {
  for (var i = 0; i < mySelector.length; i++) {
    setTimeout(function(i){
      console.log(i);
    }, 1000);
  }
}

myFunction();
<div class='mySelector'></div>
<div class='mySelector'></div>
<div class='mySelector'></div>
<div class='mySelector'></div>
<div class='mySelector'></div>

Try this:

var mySelector = document.querySelectorAll('.mySelector');
var myFunction = function() {
  for (var i = 0; i < mySelector.length; i++) {
    setTimeout(addClass(i), 1000);
  }
}

myFunction();

function addClass(i) {
  return function() {
    mySelector[i].classList.add('myClass');
  }
}
<div class='mySelector'>1</div>
<div class='mySelector'>2</div>
<div class='mySelector'>3</div>
<div class='mySelector'>4</div>
<div class='mySelector'>5</div>
NovaPenguin
  • 463
  • 3
  • 8
  • Thanks, @AndyB. This solution indeed prevents the console error! Unfortunately, 'myClass' is added to each instance of 'mySelector' all at once (after a one-second delay), rather than being added sequentially following a one-second delay each. If that makes sense. Thanks for your help, though. – Donkey Shame May 06 '17 at 14:35
  • @DonkeyShame: Change the setTimeOut time to be `1000 + 1000 * i` instead of 1000 – NovaPenguin May 06 '17 at 14:49
  • Yep, that did it! Thanks! Marked as answer. – Donkey Shame May 06 '17 at 15:30