Zombie Tab
Background notification hack for iPad and iPhone websites
About 4 min reading time
Mobile browsers may look like desktop browsers, but their behavior sometimes is different and we need to understand them to provide the right user experience. In this post I’ll show you a trick to notify the user about an update while our website is on a background tab.
At the top, Chrome for desktop showing updates on background tabs; at the bottom, Safari for iPad freezing background tabs
While on desktop browsers, we can keep multiple running tabs, on most mobile browsers timers (and all JavaScript execution) are paused when the web page left the foreground status.
If you have an iPad, let’s say you have your webmail open in one tab. When you want to browse to another website, you open a new one. That means your email will be frozen for several hours or even days and you will never get updates, until you go back to that tab.
As developers, this raises an important issue: Is there any way to update the content and notify the user while the tab is in the background? As you can see with an iOS device at http://ad.ag/pdtdpw, I’ve found a solution.
UPDATE: The quick explanation on how this hacks works is:_
Safari on iOS freezes inactive tabs, but it honors the refresh meta declaration. After a reload, the tab is alive again while it is still in the background. A kind of “background tab resurrection” process._
Updating a background tab on iPad #
After making some research and testing, I’ve found a good solution compatible with iPad to notify the user of an update when the tab is in the background: our old friend, the meta refresh.
While it can be an annoying behavior from a user’s perspective when the page is active, the old HTML mechanism allows us to define a meta tag to reload a window automatically every n seconds. Some browsers, such as Safari on iOS, allow us to use this hack to automatically reload inactive tabs and keep them updated:
<!-- Updates the page every 1 minute -->
<meta http-equiv="refresh" content="60">
With this technique, the page will reload on the iPad and the inline scripts and the onload event will be executed in the background tab on every version of iOS. On iOS 5.x no other event or timer will be executed after the onload event until the user goes back to activate the tab. On iOS 6.x timers continue executing even in the background after an automatic refresh.
Refreshing the tab only when in background #
The problem is how to remove the reload behavior when the page is the active tab and the user is using our website. Every time we set the content attribute dynamically in the meta tag, the browser starts counting again, so we shift the next reload hit. We can use this idea then to not refresh the page all the time.
To make the trick work on the iPad, we can start a chronometer that will shift the refresh meta tag n seconds on every execution. While the page is still active, our chronometer will be executed and the reload action will be shifted every n seconds. When the page goes onto a background tab, the chronometer will not be fired and the refresh meta tag will trigger, refreshing the page once. From iOS 6.1, after the refresh, the chronometer will continue executing pausing more reloads:
<!-- Updates the page every 1 minute -->
<meta http-equiv="refresh" content="60" id="metarefresh">
<script>
// iPad background tab notification trick
var mr = document.getElementById("metarefresh");
setInterval(function() {
mr.content=mr.content; // Shift the reload operation
}, parseInt(mr.content)/2); // Every 30 seconds in our example
</script>
Creating a background notification #
iOS doesn’t support the Web Notifications API as BlackBerry 10, Firefox for Android, Amazon Silk 2.0 and Firefox OS.
Unfortunately, Safari for iPad doesn’t make use of a favicon in the tab UI so our only option is to use the title element to update the UI and capture the user’s attention. Therefore we can change document.title to send information to the user via the tab’s title.
Therefore, if we just execute a chronometer and update the title, it will just work after the background reload. For example:
<script>
var count = 0;
setInterval(function() {
document.title = count;
count++
}, 1000);
</script>
In this example, the counter will start from scratch every time we move the tab to the background as it reloads once. We can use sessionStorage to maintain values between reloads.
Chrome for Android supports background execution directly; therefore these tricks are not necessary.
What about iPhone? #
I’m not sure yet if this is good news or bad news: we can use window.alert from a background tab that was refreshed and it works! And this trick also works on iPhone where there are no tabs but background windows. But be careful, it’s a very intrusive dialog from a background tab or window. If it’s an iPad, changing the title for the tab seems like a better idea.
For iPhone or iPod touch we can then refresh the page and generate an alert dialog if there is something important we want to say. However, there is no way to automatic move the active window to ours; so the alert message should indicate that to see what’s happening the user should move to our window.
Change the title only when in background #
In this example, the title is being updated all the time, even when the tab is in foreground. To change that, we need a way to separate foreground from background state after a reload. Without Page Visibility API it seems complicate. I have even tried with Animation Timing API, also known as requestAnimationFrame and even in the background, frames are being fired. At the time of this writing I couldn’t find an easy way to differentiate between foreground state and background after a reload state.
If you find a way, just add a comment in this post.
But it doesn’t work on iOS 6.0 and 5.x! #
The previous code works perfect on iPad with iOS 6.1+ but on iOS 6.0 and 5.x it’s a different story. Because timers don’t fire after the reload, we need a different approach. The approach on iOS <= 6.0 is to just update the title when the page is being loaded and leave the meta refresh make its work every n seconds. In the count example, we can just use sessionStorage.
<script>
var count = sessionStorage.getItem("count")||0;
document.title = count;
count++
sessionStorage.setItem("count", count);
</script>
In this case we don’t have the problem as in iOS 6+ and we can update the title only in the background.