user1887076
user1887076

Reputation: 13

All the setTimeouts inside javascript for loop happen at once

This function should scroll down from the top of the page to 215 pixels below, with an increasing delay, so that the first window.scrollTo event happens at 10ms, the next at 20ms, and so forth.
The last line should be delayed by 2150 ms so in all it takes about 2 seconds.
Instead, it immediately scrolls down 215 pixels all at once.

function scrollDown() {
  var yFinal=216, delay=0;
  for (y=0; y<yFinal; y++) {
    delay = delay+10
    setTimeout(function() {
      window.scrollTo(100,y);
    },delay);
  }
}

Sad face. Why?

[edit: thanks for the help! i used it to write this final solution which is a bit more complicated, and i offer it here for anyone to rip off. it scrolls fast at first, then slow. just what i wanted. by using setTimeout insteat of setInterval, it gives you even more control over the speed curve, so you could easily make it slow down exponentially]

function showCategory(categoryId)
{
  var yInitial=document.body.scrollTop,
      yFinal=216,
      delay=0;

  if (yInitial<yFinal)
  {
    yInitial=(yFinal-yInitial)/1.3+yInitial;
    window.scrollTo(100, yInitial);

    for (var yCurrent = yInitial; yCurrent < yFinal; yCurrent+=2)
    {
      delay += 30;
      (function(position)
      {
        setTimeout(function()
        {
          window.scrollTo(100, position);
        }, delay);
      })(yCurrent);
    }
  }
}

Upvotes: 1

Views: 303

Answers (2)

nnnnnn
nnnnnn

Reputation: 150010

The timeouts don't all happen at once, they happen with the timing you expect. However, they all try to scroll to a position represented by the same y variable, so they all use whatever value y has when the loop finishes.

The usual fix for this is to introduce a closure:

function scrollDown() {
    var yFinal = 216,
        delay = 0;

    for (var y = 0; y < yFinal; y++) {
        delay += 10;
        (function(position) {
            setTimeout(function() {
                window.scrollTo(100, position);
            }, delay);
        })(y);
    }
}​

(Note also that your y variable was global: you should declare it with var to make it local.)

Upvotes: 2

Blender
Blender

Reputation: 298106

You have to wrap setTimeout in an anonymous function to pass y by value and not by reference:

(function(y) {
    setTimeout(function() {
        window.scrollTo(100,y);
    }, delay);
})(y);

Upvotes: 1

Related Questions