tags:

views:

371

answers:

4

I am trying to use JQuery to iterate through a number of <li> objects using a timer, i.e. - for every 5 seconds, I would like to access the next <li> object of my <ul>.

I know you can iterate using the .each function, but I am having trouble with integrating the timer with it to iterate through each one individually.

I tried the following, but it was not working correctly. It kept giving me the last LI element. I am assuming it is because setTimeout is not sequential.

$("#div_image_thumb ul li").each(function(){    
  var myLIobj = $(this);
  setTimeout(function(){doThis(myLIobj);}, 5000);
});
+4  A: 

Something like this ought to work properly:

var elements = $("#div_image_thumb ul li");
var index = 0;

var doNext = null;
doNext = function() {
  var element = elements.eq(index);
  // do work 
  if (index < elements.length) {
    index++;
    setTimeout(doNext, 5000);
  }
}
John Fisher
While my answer may be the more elegant way, I vote for this as being more robust. One question: why do you initialize `doNext` with `null`? Is it because you need to reference it from within itself? Then `var doNext = function doNext() { … }` should be sufficient, too, right?
Fabian Neumann
or `arguments.callee` instead of `doNext` in the `setTimeout`, then it doesn't even need to be named and you can wrap it up and call it immediately.
gnarf
Yes, I created doNext before the function definition itself, so it can reference itself.
John Fisher
Is a doNext.call() line required to actually start doNext's function? It was the only way I could get your code snippet to work. Otherwise, it's awesome.
Matthew Ruston
@Matthew Ruston, all you should need is a "doNext();" statement in the appropriate place.
John Fisher
A: 

I think the problem with your code, is that you're setting many timers to expire at the same time (5 seconds from now)

So you either need to set one timer, for 5 seconds from now, and have that trigger the next 5 second timer, or you need to set a bunch of timers for different times, eg set for 5s, 10s, 15s, 20s, etc

JasonWoof
+4  A: 

John Fishers approach looks good. But if you want to stick with jQuery's each, which is quite elegant, try this (based on JasonWoof's hint to set timeouts to 5s, 10s etc.):

$("#div_image_thumb ul li").each(function (i) {
    var $li = $(this);
    setTimeout(function () { doThis($li); }, 5000 * (i + 1));
});
Fabian Neumann
Awesome! Worked perfectly. Any idea on how I can mod this so that it keeps iterating through the LI's as opposed to just 1 time ?
Bamerza
The problem was that when calling all functions "at the same time" there was a race condition and $li was messed up? BTW, why are you using $li and not just li?
elcuco
@elcuco, I suspectFabian is using $li so it is clear the variable holds an jQuery object, javascript doesn't care if it's li or $li.
Pim Jager
@Bamerza, you'll need to wrap this code in a function, then call it with another timeout after finishing this loop. It would have many similarities to the code I posted below.
John Fisher
+1  A: 

Based on your comment to the accepted answer - You want to continue looping through the collection of elements, for this you can use a setInterval and some helping functions

$(function() {
  var $lis = $("#div_image_thumb ul li");
  var index = 0;

  // setup a function to show the current one and increase the index
  var doNext = function() { 
    // assuming doThis is defined somewhere else.
    doThis($lis.eq(index));
    // increment the index - if it is beyond the number of li's - reset it to 0
    if (++index >= $lis.length) index = 0;
  };

  // call it once as we load
  doNext();
  // set it up to be called every 5000 ms
  setInterval(doNext, 5000);
});
gnarf