Building a JS automated slideshow, but loop and/or setInterval instances run concurrently?

I am attempting to build a very simple automated slideshow from scratch, but I'm coming across some difficulty. I've built working slideshows before, but not ones that were automated. So I began building one and tried using a for loop structure or the setInterval() method to mimic a loop:

$(function carousel() {
    $('.slide:not(:first-child)').hide();
    var slide1 = $('.slide:first-child');
    var slide2 = $('.slide:nth-child(2)');
    var slide3 = $('.slide:nth-child(3)');
    var slide4 = $('.slide:last-child');

    function moveSlide(currentSlide, nextSlide) {
        setInterval(function () {
            currentSlide.hide("slide", {direction: "left"}, 1000);

            setTimeout(function () {
                nextSlide.show("slide", {direction: "right"}, 1000);
            }, 1000);
        }, 1500);
    }

    var arr = [moveSlide(slide1, slide2), moveSlide(slide2, slide3), moveSlide(slide3, slide4)];
    var i = 0;
    setInterval(function () {
        if (i < arr.length) {
            arr[i] += 1;
            console.log(i + "=>" + arr[i]);
        } else {
            return;
        }
        i++;
    }, 1500);
});

Here's a Codepen.

Unfortunately, this did not go well, and I know why. I understand that in JS, the code will continue to execute and will not wait for the information in a loop to finish if using setInterval or setTimeout. So my question is, what would be a good workaround that doesn't require using an external library or plugin? If you could try to stick as close to my source code as possible that would be awesome. Thanks!

Answers 1

  • There are a few problems with your code. Calling moveSlide() will hide the specified slide and (after the timeout) show the specified next slide, but your use of setInterval() within that function means it will keep trying to hide the same first slide and then show the next one.

    The line with var arr = [moveSlide(slide1, slide2),... is calling the moveSlide() function immediately and putting its return values into the array. So that means you've got several intervals all running (one per call to moveSlide()), and all stepping over each other trying to hide and show the same elements. Also the return value is undefined, so basically you've got an array full of undefined.

    What I would suggest you do instead is something like the following:

        $(function carousel() {      
            // get a list of *all* slides:
            var slides = $('.slide');
            // hide all but the first:    
            slides.slice(1).hide(); 
            var current = 0;
        
            setInterval(function() {
              // hide the current slide:
              slides.eq(current).hide(1000);
              // increment the counter, wrapping around from end of the
              // list to the beginning as required:
              current = (current + 1) % slides.length;
              // show the next slide after a timeout:
              setTimeout(function () {
                 // note that `current` was incremented already:
                 slides.eq(current).show(1000);
              }, 1000);
            }, 3500); // make the interval larger than the hide/show cycle
        });
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
    <div class="slide">Slide 1</div>
    <div class="slide">Slide 2</div>
    <div class="slide">Slide 3</div>
    <div class="slide">Slide 4</div>
    <div class="slide">Slide 5</div>

    Notice that I don't need individual variables for the individual slides, I just have a single slides variable that is a jQuery object containing all of the slides. This means you can easily change the number of slides on the page without changing the JS at all.

    Note that I was too impatient to get jQueryUI to work in the snippet so I've just used a simple .hide() and .show(), but obviously that isn't the important part of the code I've shown.


Related Articles