1

I've built a little carousel programme in vanilla JavaScript and it's working great, except when I visit another browser window and then come back to my carousel window.

The slides race past each other, as though they've been waiting to run while I've been elsewhere, until it reaches the slide it would have been at had I been watching the whole time, which often slides in from the wrong side. Then it settles down and continues to work as expected. This behaviour is the same in Chrome and Firefox.

The full code is on GitHub at https://github.com/lucywho/lightweight-carousel but this is the relevant section of the script:

const slide = document.querySelectorAll("#carousel .carousel-slide");
    const dots = document.querySelectorAll("#dots .dot");
    const total = slide.length;
    let last = total - 1;
    let i = 0;
    let timer;
    let isTransitioning = false;

    timer = setTimeout(moveSlide, 5000);

    function moveSlide(index) {
        isTransitioning = true;

        if (typeof index == "number") {
            if (i === index) {
                return;
            } else {
                slide[i].classList.remove("onscreen");
                dots[i].classList.remove("fill-in");
                slide[i].classList.add("offscreen-left");

                i = index;

                dots[i].classList.add("fill-in");
                slide[i].classList.add("onscreen");
            }
        } else if (i < last) {
            slide[i].classList.remove("onscreen");
            dots[i].classList.remove("fill-in");
            slide[i].classList.add("offscreen-left");

            i++;

            dots[i].classList.add("fill-in");
            slide[i].classList.add("onscreen");
        } else if (i === last) {
            slide[i].classList.remove("onscreen");
            dots[i].classList.remove("fill-in");
            slide[i].classList.add("offscreen-left");

            i = 0;

            dots[i].classList.add("fill-in");
            slide[i].classList.add("onscreen");
        }

        timer = setTimeout(moveSlide, 7000);
    }

document.addEventListener("transitionend", function(event) {
        if (event.target.classList.contains("offscreen-left")) {
            event.target.classList.remove("offscreen-left");
            isTransitioning = false;
        }

Am I right in thinking that the problem is something to do with the script continuing to run in the background even when the page is not displayed? And if so, how do I force JavaScript to pause? (I tried wrapping it all in a document.addEventListener("DOMContentLoaded", function(){} but that didn't help)

Many thanks in advance.

ETA: thanks to Andre, I've tried the following approach. Again, it runs happily when the page loads but I'm still getting a few seconds of weirdness when I return from another tab.

let screenVis = true;

if (screenVis) { 
  ... 
  carousel code
  ... 
}

 document.addEventListener("visibilitychange", function() {
    if (document.hidden === true) {
        screenVis = false;
    } else {
        screenVis = true;
    }
});

So, either I've mucked up my code (always possible) or something else is going on?

lucy_who
  • 73
  • 6
  • I think if you listen to ["visibilityChange"](https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API) instead of DOMContentLoaded event it may work. – André Adriano Aug 12 '20 at 13:01
  • 1
    Yes, the code keeps running in the background since it's based entirely on timing. All your visual changes get queued up and fires all at once when the page gains visibility. The easiest way to fix this is to only trigger the next iteration when the page is visible. This can be done with `requestAnimationFrame()`, like so: `window.requestAnimationFrame(function() { timer = setTimeout(moveSlide, 7000); });` – Lennholm Aug 12 '20 at 16:05
  • Thank you so much Lennholm, that is exactly what I was looking for! Unfortunately I don't have voting privileges yet, but fwiw consider this an "accepted" answer – lucy_who Aug 14 '20 at 09:05

0 Answers0