Search code examples
javascriptjqueryarraysloopswait

Iterate through an array of DIVs and fadeIn/fadeOut


I want to build a simple banner image slider functionality, but instead of sliding, the images are supposed to fade in and out.

It's a Drupal project, so I don't know how many images are going to be used.
The structure is simple enough:

<div class="banner-container">
  <!-- potentially a lot of divs with background images -->
</div>

What I want to do is essentially iterate through this "array" of divs let the currently active one fade out and let the one that the array is pointing to at the moment fade in.

I thought I could get it to work like this:

var a = $('#banner-container').children();

while(1) {
  a.each(function(){
    $('.active').fadeOut(500).removeClass('active');
    $(this).fadeIn(500).addClass('.active');
  })
}

Obviously there is a lot wrong in my code, I have run into multiple issues, but the one I cannot seem to solve is getting the loop to wait between the iterations. To my knowledge, a traditional loop like foreach could just have some sort of wait function added to the end of it and execute that wait in every loop. But the .each() seems to execute every iteration simultaneously. How can I loop through an array like this (potentially infinitely) and wait like 5 seconds every time?

I read somewhere that the animate functions work asynchronously, and I assume the same goes for CSS transforms, so creating fadeIn, and fadeOut classes and try this with CSS wouldn't work.


Solution

  • You're looking for the setTimeout and setInterval functions: You pass a function into them with a timeout (in milliseconds), and they call the function after that many milliseconds (repeating, in the case of setInterval).

    So for instance:

    var a = $('#banner-container').children();
    var index = 0;
    run();
    function run() {
      a.filter('.active').fadeOut(500).removeClass('active');
      a.eq(index).fadeIn(500).addClass('active');
      index = (index + 1) % a.length; // Wraps around if it hits the end
      setTimeout(run, 1000);
    }
    

    That will update the first div, then a second later the next div, and so on.

    Live Example:

    // Scoping function to avoid creating globals
    (function() {
      var a = $('#banner-container').children();
      var index = 0;
      run()
    
      function run() {
        a.filter('.active').fadeOut(500).removeClass('active');
        a.eq(index).fadeIn(500).addClass('active');
        index = (index + 1) % a.length; // Wraps around if it hits the end
        setTimeout(run, 1000);
      }
    })();
    #banner-container a {
      display: none;
      position: absolute;
    }
    #banner-container {
      position: relative;
    }
    <div id="banner-container">
      <a>one</a>
      <a>two</a>
      <a>three</a>
      <a>four</a>
      <a>five</a>
    </div>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>

    In the above, I've used setTimeout, which only schedules a single callback, because I find I write fewer bugs when the thing I'm calling has to explicitly re-schedule the next call. :-) But you could also use setInterval(run, 1000); which would call run at roughly one-second intervals, until/unless you told it to stop via clearInterval.