22

Is there a way to stop setTimeout("myfunction()",10000); from counting up when the page isn't active. For instance,

  1. A user arrives at a "some page" and stays there for 2000ms
  2. User goes to another tab, leaves "some page" open.
  3. myfunction() doesn't fire until they've come back for another 8000ms.
Akhil Jain
  • 13,872
  • 15
  • 57
  • 93
Kyle Cureau
  • 19,028
  • 23
  • 75
  • 104

6 Answers6

41
(function() {
  var time = 10000,
      delta = 100,
      tid;

  tid = setInterval(function() {
    if ( document.hidden ) { return; }    
    time -= delta;
    if ( time <= 0 ) {
      clearInterval(tid);
      myFunction(); // time passed - do your work
    }        
  }, delta);
})();

Live demo: https://jsbin.com/xaxodaw/quiet


Changelog:

  • June 9, 2019: I’ve switched to using document.hidden to detect when the page is not visible.
Šime Vidas
  • 182,163
  • 62
  • 281
  • 385
  • Unless I'm missing something, this will not run setInterval functions on initial page load before any user interactions such as clicks on the page, since the `onfocus` event does not appear to fire on initial page load. –  May 17 '13 at 06:07
  • @torazaburo Initially, `window.blurred` will be `undefined` which is the same as if it were `false`. Hence, the timer starts counting down automatically. Note how the `setInterval` executes immediately - it doesn't wait for a focus or blur to occur. – Šime Vidas May 17 '13 at 13:33
  • 1
    You can use document.hidden: https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API#document.hidden_Read_only – SleepWalker Mar 13 '17 at 11:57
  • This does not work in my Chrome 74.0.3729.169. The timer gets completed even when I am not focused in the relevant tab (albeit in a much slower time) – George Vasilopoulos Jun 06 '19 at 20:19
  • @GeorgeVasilopoulos How are you un-focusing the tab? (jsbin:xaxodaw) – Šime Vidas Jun 07 '19 at 13:52
  • @ŠimeVidas i have multiple tabs open, start the timer and then change my tab. It will not take only 10 seconds, however in around a minute or something, the tab with the timer will get an bluish dot (indicating a notification) and when I refocus the alert has popped and the timer shows 0. – George Vasilopoulos Jun 09 '19 at 10:58
  • 1
    @GeorgeVasilopoulos I’ve updated the code and demo. Please let me know if it still doesn’t work as expected. – Šime Vidas Jun 09 '19 at 14:15
  • This didn't work for me if I open another program and the browser window is not visible but the page executing the interval is the active browser tab. I've posted another answer fixing this issue. – Caumons May 08 '20 at 16:21
8

Great answer by Šime Vidas, it helped me with my own coding. For completeness sake I made an example for if you want to use setTimeout instead of setInterval:

(function() {

    function myFunction() {
        if(window.blurred) {
            setTimeout(myFunction, 100);
            return;
        }

        // What you normally want to happen

        setTimeout(myFunction, 10000);
    };
    setTimeout(myFunction, 10000);

    window.onblur = function() {window.blurred = true;};
    window.onfocus = function() {window.blurred = false;};

})();

You'll see that the window blurred check has a shorter time set than normal, so you can set this depending on how soon you require the rest of the function to be run when the window regains focus.

Jasuten
  • 1,570
  • 12
  • 20
6

You can do something like:

$([window, document]).blur(function() {
  // Clear timeout here
}).focus(function() {
  // start timeout back up here
});

Window is for IE, document is for the rest of the browser world.

A1rPun
  • 16,287
  • 7
  • 57
  • 90
PetersenDidIt
  • 25,562
  • 3
  • 67
  • 72
  • thank you for this reply. I used the other as it fulfilled all the reqs. But about this one, I had more success with `$(window)` as the object for all browsers. – Kyle Cureau Apr 26 '11 at 20:57
3

I use almost the same approach as Šime Vidas in my slider but my code is based on document.visibilityState for page visibility checking:

document.addEventListener("visibilitychange", () => {

  if ( document.visibilityState === "visible" ) {

    slideshow.play();

  } else {

    slideshow.pause();
  }
});
BroFox
  • 61
  • 4
1

What you'd have to do is set up a mechanism to set timeouts at small intervals, keeping track of total elapsed time. You'd also track "mouseenter" and "mouseleave" on the whole page (the <body> or something). When the short-term timeouts expire, they can check the window state (in or out) and not restart the process when the window is not in focus. The "mouseenter" handler would start all paused timers.

edit — @Šime Vidas has posted an excellent example.

Pointy
  • 405,095
  • 59
  • 585
  • 614
1

I've finally implemented a variation of @Šime Vidas' answer, because the interval was still running if I opened another program and the browser window was not visible, but the page executing the interval was the active browser tab. So, I've modified the condition to document.hidden || !document.hasFocus(). This way, if the document is hidden or the document doesn't have the focus, the interval function just returns.

(function() {
  var time = 10000,
      delta = 100,
      tid;

  tid = setInterval(function() {
    if ( document.hidden || !document.hasFocus() ) { return; }    
    time -= delta;
    if ( time <= 0 ) {
      clearInterval(tid);
      myFunction(); // time passed - do your work
    }        
  }, delta);
})();
Caumons
  • 9,341
  • 14
  • 68
  • 82