Introduction to Page Visibility API — SitePoint (original) (raw)
Key Takeaways
- The Page Visibility API allows site developers to determine the current visibility state of the page, aiding the development of powerful and CPU efficient web applications, particularly for mobile devices with limited resources.
- The API includes a single event called ‘visibilitychange’ and two read-only properties, ‘hidden’ and ‘visibilityState’, which can indicate whether a page is visible, hidden, prerendered, or about to be unloaded.
- The Page Visibility API is not universally supported across all browsers, and those that do often use vendor prefixes, leading to potential compatibility issues. Polyfills like visibly.js and isVis.js can be used to bridge this gap in unsupported browsers.
- Practical use cases for the Page Visibility API include pausing video or audio playback when a user switches tabs, halting data fetches or animations to conserve CPU and bandwidth, and tracking user engagement by logging the time spent on the page.
Mobile is cool. Mobile apps are even cooler. Unfortunately, in most cases, mobile connections suck because they are slow or you don’t haven unlimited bandwidth. It would be great to have rich web applications that don’t waste users’ resources, especially when they aren’t looking at that page. This article will show you how to partially solve this and other problems using the Page Visibility API.
In the last few years, several new great APIs have been introduced to help us in our everyday work, such as Geolocation API, Navigation Timing API and Full-screen API. Page Visibility API defines a means for site developers to programmatically determine the current visibility state of the page in order to develop powerful and CPU efficient web applications. From July 26th 2012, it’s a W3C Candidate Recommendation so it’s considered stable.
The first thing you might wonder is how they improve performance and save bandwidth. Imagine a situation where you have a great AJAX-based web application that send data back and forth every five seconds. If the user sends the browser tab to background when your application is running, it’ll still send data every five seconds, and also if the user takes the tab in foreground after 10 minutes. Wouldn’t it be great if the application slowed down the updates or stopped them until the user looked at the page again? Here’s where the resources optimization comes in, and where the Page Visibility API plays the key role.
How Page Visibility APIs are made
These APIs are quite simple, in fact they have a single event called visibilitychange
and two read-only properties belonging to document
, hidden
and visibilityState
. hidden
is a boolean value which is true
if the page is not visible, even the smallest part, and this typically happens when the tab is in background or the browser is minimized. It’s important to note that this rule has some exceptions for accessibility tools that act in full-screen mode. You can find out more on this by reading the hidden specifications.
visibilityState
is an enumeration that specifies the current state of the document and consists of the following values:
hidden
: The document is not visible at allvisible
: The document or a part of it is visibleprerender
: The document is loaded off-screen and isn’t visibleunloaded
: The document is going to be unloaded
Please note that the last two values, prerender
and unloaded
, are optional. Besides, like the hidden
attribute, the hidden
value has some exceptions regarding assistive technologies.
Compatibility
Currently, there aren’t many browsers that support these APIs and those that do still use vendor prefixes. This leads to support problems because you have to manage all the prefixes to have a working code. Currently the desktop browsers that support the Page Visibility API are Chrome 13+, Internet Explorer 10, Firefox 10+, Opera beta 12.10. The mobile browsers that support the API are Chrome on Android 4.0+ and Opera Mobile 12.1+ on both Android and Symbian (source MobileHTML5.org – tested by myself on Android 4.0).
A mildly annoying point is that due to the camelCase convention, if the properties are vendor prefixed, the actual property name has the first letter capitalized, while it’s lowercase if it isn’t prefixed. For the sake of clarity, let’s take the hidden
property. As you can see, it starts with a lowercase letter but if it’s prefixed it starts with an uppercase “h”, so to test the support you can’t write code that resembles the following:
var browserPrefixes = ["", "webkit","moz","ms","o"]; for(var i = 0; i < browserPrefixes.length; i++) { if (document[browserPrefixes[i] + "hidden"] != undefined) // here goes the code }
And you have to split the cases, like the following, or use some trick against strings.
// Test for unprefixed version if (document.hidden !== undefined) // here goes the code else { // Test for prefixed version var browserPrefixes = ["webkit", "moz", "ms", "o"]; for(var i = 0; i < browserPrefixes.length; i++) { if (document[browserPrefixes[i] + "Hidden"] != undefined) { // here goes the code } } }
As always, just like other APIs, a bunch of polyfills have been released to use those APIs in browsers that don’t support them. Some of these polyfills are visibly.js and isVis.js.
Let’s create a working example
In this section, I’ll guide you through creating a simple demo page that uses the Page Visibility API. The page will firstly test for browser support and then count the times the user actually sees the page and log its states. There are just three key functions in our demo. The first tests if the browser uses a vendor prefixed version or not, and that will be created on top of the last code shown. It will return an empty string if the browser uses the unprefixed version, the vendor prefix if it uses a prefixed version, or null
in the case that the browser doesn’t support the API.
function getPrefix() { var prefix = null; if (document.hidden !== undefined) prefix = ""; else { var browserPrefixes = ["webkit","moz","ms","o"]; // Test all vendor prefixes for(var i = 0; i < browserPrefixes.length; i++) { if (document[browserPrefixes[i] + "Hidden"] != undefined) { prefix = browserPrefixes[i]; break; } } } return prefix; }
The second function logs the state and increments the view count when the page is displayed.
function countView() { // The page is in foreground and visible if (document.hidden === false || document[prefix + "Hidden"] === false) views++;
document.getElementById("log").innerHTML += "Your view count is: " + views + ". " + "Your page current state is: " + document[(prefix === "" ? "v" : prefix + "V") + "isibilityState"] + " "; }
The third and last function tests whether the browser supports the API and if the test is true
, it attaches a handler to the visibilitychange
event, or otherwise it notifies the user. Please note that this function, too, is needed to manage the vendor prefixes.
function testPageVisibilityApi() { if (prefix === null) document.getElementById("log").innerHTML = "Your browser does not support Page Visibility API"; else { document.addEventListener(prefix + "visibilitychange", countView); countView(); } }
Putting it all together
Given the functions shown in the previous section, the final and completely working code is the following.
Page Visibility API Test Page by Aurelio De Rosa