SetTimeout inside a for loop

微笑、不失礼 提交于 2020-01-25 23:43:26

问题


I'm trying to write a script that changes the z-index of 3 images. Basically the script should target the current image and apply a higher z-index on the next image, like a sort of carousel but with a z-index rather then active class. The challenge is to set the z-index after a specific interval. The problem is that the first image is displayed and then the last one. This is my code:

Html:

<div class="changingimages">
    <img src="#" data-time="3000" width="100%" class="alternateimage alternateimage1">
    <img src="#" data-time="2000" width="100%" class="alternateimage alternateimage2">
    <img src="#" data-time="4000" width="100%" class="alternateimage alternateimage3">
</div>

jQuery Script

<script type="text/javascript">

jQuery(document).ready(function(){

    var changeImg = function(i, time, currentImg) {

        setTimeout(function(){

            jQuery(currentImg).next().css("z-index", i);

        }, time);
    };

    var numberOfChilds = jQuery(".changingimages").children().length;
    var currentIndexClass;
    var currentImg;
    var time;

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

            currentIndexClass = '.alternateimage' + i;
            currentImg = jQuery(currentIndexClass);
            time = jQuery(currentIndexClass).attr("data-time");

            changeImg(i, time, currentImg);

    }

});

I think there is some problem with the closure inside a loop, but not sure!


回答1:


It's a common misconception that setTimeout schedules events to run relative to previously queued events. It looks like you believe that, theoretically, the following:

setTimeout(f, 100);
setTimeout(g, 100);
setTimeout(h, 100);

would result in a timeline like this:

0ms   Start
100ms Run f()
200ms Run g()
300ms Run h()

The reality is that the time option in setTimeout means "run this function after at least this much time has passed." Going off of the previous example, you would actually get something more like

0ms   Start
100ms Run f()
101ms Run g()
102ms Run h()

To space out your code correctly, keep adding to the timeout time rather than replacing it.

var time = 0;

for (var i = 1; i <= numberOfChilds; i++) {
  currentIndexClass = '.alternateimage' + i;
  currentImg = jQuery(currentIndexClass);

  // Add to the previous time
  time += parseInt(jQuery(currentIndexClass).attr("data-time"), 10);
  changeImg(i, time, currentImg);
}



回答2:


Here is a fiddle implementing the use of timeout to achieve what you want.

fiddle

.textArea {
  position: absolute;
  height: 50px;
  width: 50px;
  display: block;
}

.box_a {
  background-color: blue;
}

.box_b {
  background-color: red;
}

.box_c {
  background-color: orange;
}

.active {
  z-index: 3;
}


<div class="textArea box_a active">a</div>
<div class="textArea box_b">b</div>
<div class="textArea box_c">c</div>

$(function(){
  var $elem = $('.textArea');

  timeout(0);

  function timeout(i){
    $($elem[i]).addClass('active');
    return setTimeout(function(){
      $elem.removeClass('active');
      i++;
      if(i >= $elem.length){
        i = 0
      }
      timeout(i);
    }, 1000)
  }
});

Note it does not use a for loop, because timeout is asynchronous and will not execute sequentially. Each timeout will fire at the same time basically, then do their action based on the wait time.

The solution is to make a function that keeps track of the index, and when the last timeout has completed execution.



来源:https://stackoverflow.com/questions/40026553/settimeout-inside-a-for-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!