This article explains the potential pitfalls with standard web timers, and offers a small, free drop-in replacement: setVisiTimeout()
"The only reason for time is so that everything doesn't happen at once" – Albert Einstein
When building a web app, developers often make use of timers. Timers allow programmatic events based on an elapsed time. This can be used in very visible ways, such as a notification popup that hides itself after some amount of time; or in less visible ways, such as a resource load timeout.
But what about when the user isn't watching our page? Perhaps they have tabbed to a different window, or minimized the browser to use some other app. Or maybe they've even put their device to sleep.
The result is often a poor user experience.
In either case, the behavior is likely undesired.
For example, if the timer is being used for a notification that should be displayed for 10 seconds and then hidden, the user needs to have a chance to read it. Which means what we really want is 10 seconds of user visibility. Ideally, if the user stops viewing the page, the timer should pause; and then resume once the user returns.
This behavior is exactly what you get with setVisiTimeout() - a function you can copy/paste into your apps and use in place of setTimeout() to provide a better user experience.
It uses the Page Visibility API to detect visibility events, such as the user minimizing the browser, switching to another tab, or hiding the browser window with another app.
Clicking the blue button below will turn it red for 4 seconds, and then it will return to blue. It is using setVisiTimeout() so modern browsers will pause if you hide this window:
Some readers may be wondering about that resource timer I mentioned in the first paragraph. What might that have to do with page visibility?
In the case of mobile browsers, like Safari on iPhone, the browser suspends all network activity for the page not being viewed. So for resources that use setTimeout() to give up on the resource (such as in requireJS), this is a common scenario:
So use setVisiTimeout() for resource loading timeouts as well if you can. If you are using a resource loader such as requireJS, you can set the timeout to 0 and then wire up your own setVisiTimer() event that checks to see if the resource loaded and if not, notify the user.
Ok, here is the code for setVisiTimeout(). It is also available at GitHub.
This has been tested in a handful of browsers, and seems to work well in those that support the PageVisibility API. Those that don't fall back to using a standard timer. It even supports IE8 (though this page doesn't render properly - to see IE8 working, see this test page.)
Note: The visibility event does not fire if the browser window is partially covered. Additionally, there is obviously no way to know if the user is simply distracted and looking away or has left their chair for a coffee refill. Design accordingly! ;-)