timkl
timkl

Reputation: 3339

Looping setTimeout

I'm currently trying to wrap my head around some JavaScript.

What I want is a text to be printed on the screen followed by a count to a given number, like so:

"Test"

[1 sec. pause]

"1"

[1 sec. pause]

"2"

[1 sec. pause]

"3"

This is my JS:

$(document).ready(function() {

    var initMessage = "Test";
    var numberCount = 4;    

function count(){

    writeNumber = $("#target");

    setTimeout(function(){
        writeNumber.html(initMessage);
    },1000);

        for (var i=1; i < numberCount; i++) {

    setTimeout(function(){
        writeNumber.html(i.toString());
    },1000+1000*i)};

};

 count();

});

This is my markup:

<span id="target"></span>

When I render the page, all I get is "Test" followed by "4".

I'm no JavaScript genius, so the solution could be fairly easy. Any hints on what is wrong is highly appreciated.

You can play around with my example here: http://jsfiddle.net/JSe3H/1/

Upvotes: 5

Views: 10479

Answers (3)

Andrew Whitaker
Andrew Whitaker

Reputation: 126042

You have a variable scope problem. The counter (i) inside the loop is only scoped to the count function. By the time the loop has finished executing, is value is 4. This affects every setTimeout function, which is why you only ever see "4".

I would rewrite it like this:

function createTimer(number, writeNumber) {
    setTimeout(function() {
        writeNumber.html(number.toString());
    }, 1000 + 1000 * number)
}

function count(initMessage, numberCount) {
    var writeNumber = $("#target");

    setTimeout(function() {
        writeNumber.html(initMessage);
    }, 1000);

    for (var i = 1; i < numberCount; i++) {
        createTimer(i, writeNumber);
    }
}

$(document).ready(function() {

    var initMessage = "Test";
    var numberCount = 4;


    count(initMessage, numberCount);

});

The createTimer function ensures that the variable inside the loop is "captured" with the new scope that createTimer provides.

Updated Example: http://jsfiddle.net/3wZEG/

Also check out these related questions:

Upvotes: 6

David Hedlund
David Hedlund

Reputation: 129792

In your example, you're saying "2, 3, 4 and 5 seconds from now, respectively, write the value of i". Your for-loop will have passed all iterations, and set the value of i to 4, long before the first two seconds have passed.

You need to create a closure in which the value of what you're trying to write is preserved. Something like this:

for(var i = 1; i < numberCount; i++) {
    setTimeout((function(x) {
        return function() {
            writeNumber.html(x.toString());
        }
    })(i),1000+1000*i)};
}

Another method entirely would be something like this:

var i = 0;
var numberCount = 4;

// repeat this every 1000 ms
var counter = window.setInterval(function() {

   writeNumber.html( (++i).toString() );

   // when i = 4, stop repeating
   if(i == numberCount)
       window.clearInterval(counter);

}, 1000);

Upvotes: 3

Sudhir Bastakoti
Sudhir Bastakoti

Reputation: 100175

Hope this helps:

var c=0;
var t;
var timer_is_on=0;

function timedCount()
{
document.getElementById('target').value=c;
c=c+1;
t=setTimeout("timedCount()",1000);
}

function doTimer()
{
if (!timer_is_on)
  {
  timer_is_on=1;
  timedCount();
  }
}

Upvotes: 1

Related Questions