Skip to content Skip to sidebar Skip to footer

Setinterval 'tripping' Itself And Starting A 2nd Interval Timer

I've coded a tab function that switches between tabs after a set period of time, this seemingly worked perfectly however, it's been bought to my attention that sometimes it breaks.

Solution 1:

If you just simplify your logic to only have a single interval running, all your syncronisation problems go away

const tabs = [
  {title: tabTitle1, content: tabContent1},
  {title: tabTitle2, content: tabContent2},
  {title: tabTitle3, content: tabContent3},
  {title: tabTitle4, content: tabContent4}
 ];
 
 let curr = 0;
 let tab = tabs[curr];
 tab.title.addClass("active");
 tab.content.addClass("active");

 var timerInterval = setInterval(function(){     
        tab.title.removeClass("active");
        tab.content.removeClass("active");
        curr = ++curr % tabs.length;
        tab = tabs[curr];
        tab.title.addClass("active");
        tab.content.addClass("active");
 }, tabLength);

Live example

jQuery(document).ready(function() {
  functionautoPlayTabs(tabTitle1, tabTitle2, tabTitle3, tabTitle4, tabContent1, tabContent2, tabContent3, tabContent4, tabLength, tabTitleMobile1, tabTitleMobile2, tabTitleMobile3, tabTitleMobile4) {
    var actualTabLength = tabLength * 4;
    var tabContainer = jQuery('.elementor-tabs');
    var allTabs = jQuery('.elementor-tabs .elementor-tabs-wrapper .elementor-tab-title');
    var allContent = jQuery('.elementor-tabs .elementor-tabs-content-wrapper .elementor-tab-content');
    var initialTabTimer = null;
    var tabTimer = null;
    
    const tabs = [
        {title: tabTitle1, content: tabContent1},
      {title: tabTitle2, content: tabContent2},
      {title: tabTitle3, content: tabContent3},
      {title: tabTitle4, content: tabContent4}
     ];
     
     let curr = 0;
     let tab = tabs[curr]
     tab.title.addClass("active")
     tab.content.addClass("active");
     var timerInterval = setInterval(function(){
     
            tab.title.removeClass("active")
            tab.content.removeClass("active");
        curr = ++curr % tabs.length
            tab = tabs[curr]
        tab.title.addClass("active")
            tab.content.addClass("active");
     }, tabLength)
    

    allTabs.click(function() {
      if (initialTabTimer !== null) {
        clearTimeout(initialTabTimer);
        initialTabTimer = null;
      }

      if (tabTimer !== null) {
        clearInterval(tabTimer);
        tabTimer = null;
      }

      allTabs.removeClass('active');
      allContent.removeClass('active');
      tabContainer.addClass('tabsManual');
    });
  }

  if ($("#homeTabTitle1")) {
    autoPlayTabs($("#homeTabTitle1"), $("#homeTabTitle2"), $("#homeTabTitle3"), $("#homeTabTitle4"), $("#homeTabContent1"), $("#homeTabContent2"), $("#homeTabContent3"), $("#homeTabContent4"), 1000);
  }
});
.active { background-color:red }
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><divid="homeTabTitle1">
  homeTabTitle1
</div><divid="homeTabTitle2">
  homeTabTitle2
</div><divid="homeTabTitle3">
  homeTabTitle3
</div><divid="homeTabTitle4">
  homeTabTitle4
</div><divid="homeTabContent1">
  homeTabContent1
</div><divid="homeTabContent2">
  homeTabContent2
</div><divid="homeTabContent3">
  homeTabContent3
</div><divid="homeTabContent4">
  homeTabContent4
</div>

Note I have not hooked up your "stop" logic, but all you need to do is clearInterval(timerInterval) in your click handler.

Solution 2:

As requested

Could you pop an updated answer of that using setInterval

You can refactor to a single setInterval(). The following is a concept - if your tabs are not next to each other in the HTML, then you can collate them up-front and loop through an array. You can also link your content to your tab using data- attributes.

This also allows your use to change the "active" tab (eg by clicking on it) and it will "auto play" to the next one each time - but you might want to reset the timer in that case.

var interval_time = 250;  // short time for demovar tabs = $(".tab");

// single setIntervalvar timer = setInterval(() => {

  // get the currently active, so no need to store what that wasvar active = tabs.filter(".active");
  active.removeClass("active");

  // get the next tab, if none, then loop back to the firstvar next = active.next(".tab");
  if (next.length == 0)
    next = tabs.first();
  next.addClass("active");

}, interval_time);

$("#stop").click(() =>clearInterval(timer))
/* can show / hide
.tab { display:none; }
.tab.active { display:block; }
*//* or show all the tabs at once */.tab { display:inline-block; color: #CCC; }
.tab.active { color: green }
<scriptsrc="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script><divclass='tab active'>tab 1</div><divclass='tab'>tab 2</div><divclass='tab'>tab 3</div><divclass='tab'>tab 4</div><br/><buttontype='button'id='stop'>stop</button>

Solution 3:

This is not setTimeout tripping up, it is setTimeout being called twice

To help document this, you could put console.logs in to show when each timer is being started. For simplicity, I am also getting rid of all the class adding and removals.

I am also curious where homeTabTitle1 comes from - could you clarify?

jQuery(document).ready(function() {
  functionautoPlayTabs(tabTitle1, tabTitle2, tabTitle3, tabTitle4, tabContent1, tabContent2, tabContent3, tabContent4, tabLength, tabTitleMobile1, tabTitleMobile2, tabTitleMobile3, tabTitleMobile4) {
    var actualTabLength = tabLength * 4;
    var tabContainer = jQuery('.elementor-tabs');
    var allTabs = jQuery('.elementor-tabs .elementor-tabs-wrapper .elementor-tab-title');
    var allContent = jQuery('.elementor-tabs .elementor-tabs-content-wrapper .elementor-tab-content');
    var initialTabTimer = null;
    var tabTimer = null;

    console.log("Setting initialTabTimer")
    initialTabTimer = setTimeout(function() {

      console.log("Setting timer 2")
      setTimeout(function() {

        console.log("Setting timer 3")
        setTimeout(function() {

          console.log("Executing inner function 3")
        }, tabLength);
      }, tabLength);
    }, tabLength);

    console.log("Setting tabTimer")
    tabTimer = setInterval(function() {

      console.log("Setting timber B")
      setTimeout(function() {

        console.log("Setting timber C")
        setTimeout(function() {

          console.log("Setting timber D")
          setTimeout(function() {

            console.log("Executing inner function D")
          }, tabLength);
        }, tabLength);
      }, tabLength);
    }, actualTabLength);

    allTabs.click(function() {
      if (initialTabTimer !== null) {
        clearTimeout(initialTabTimer);
        initialTabTimer = null;
        console.log("Cleared initialTabTimer");
      } else {
        console.log("Did not need to clear initialTabTimer");
      }

      if (tabTimer !== null) {
        clearInterval(tabTimer);
        tabTimer = null;
        console.log("Cleared tabTimer");
      } else {
        console.log("Did not need to clear tabTimer");
      }

    });
  }

  if (homeTabTitle1) {
    console.log("calling AutoPlayTabs")
    autoPlayTabs(homeTabTitle1, homeTabTitle2, homeTabTitle3, homeTabTitle4, homeTabContent1, homeTabContent2, homeTabContent3, homeTabContent4, homeTabLength);
  }
});

Why might document ready fire more than once?

As pointed out here:

jQuery $(document).ready () fires twice

The commonest cause is something being manipulated in the DOM that causes the document to be re-rendered. The answerer suggests a solution.

enter image description here

Why do some people call setTimeout "unreliable"?

I think they mean you cannot rely on when the callback function will be called. I think they unreasonably expect setTimeout to be able to magically cause Javascript to run the code exactly at the desired time. In reality, setTimer simply puts the task on a list of tasks to be run when the main program has run out of things to do.

Typically, on a web page, there is almost nothing for the Javascript interpreter to do after the page is initially rendered. In that case, the setTimeout callbacks will be called at a reasonably accurate time.

However, if the web page has plenty of things for the Javascript interpreter to do, taking many seconds or even minutes of "thinking time" such as massive calculations, then setTimeout will appear to be unreliable at a superficial assessment. However it is not really unreliable, it is doing what it should do, which is delaying the callback until there is both free time, and sufficient time has passed.

If the Javascript code calls asynchronous functions, then that potentially frees up the interpreter to look at setTimeout events. So if the web page becomes ready (from a jQuery point of view) but then has several seconds of communication with other sites to get database entries etc then, as long as those database calls are asynchronous, setTimeout is able to fire its callback if needed.

setInterval and setTimeout are different

"For some reason timeoutB has been fired again"

enter image description here

The reason is that it is a setInterval, not a setTimeout. Was that intentional? setInterval will keep firing over and over.

Post a Comment for "Setinterval 'tripping' Itself And Starting A 2nd Interval Timer"