Sunday, January 1, 2012

Can you see me? Can you see me now?

What is the Page Visibility API and what is it good for? It is a JavaScript API that allows developers to check if their page/application is visible and to attach an event listener for visibility state changes. What is that useful for? Well, lets discuss web applications in general for a moment to see how and why it is something developers should be concerned with.

Every window or tab a user opens for services like Google+, Facebook, Gmail, Twitter, and Reddit increases the resource requirement for each application to poll, update, animate and perform other intensive operations. The big question is do end-users really have to pay the resource costs for each of these web applications?

To answer this question, we can ask a few more questions about web applications in general:
  1. Do they need to constantly run?
  2. What operations need to be run and at what rate?
  3. Can these operation rates be altered based on interaction types/levels?
  4. What does user interaction entail?
First, let me state this is not a blanket solution nor an advocacy that all applications should be handled in this manner. That said, developers should think about not only how their applications are actively used but also what they need to do when not in active use.

Second, lets use a simple example to provide a context for pondering these questions. A basic news or message feed would work well enough. It is based around a data stream that is updated both asynchronously and irregularly so some form of polling and updating the stream with new items is required. Also, dynamic animation showing new items will be used to display these updates to an end-user.

Finally, the first three questions are in one way or another predicated on the answer for the fourth so we will begin there.

What does user interaction entail? The obvious cases are when a user is interacting with the application using an input device (mouse, keyboard, mic, camera, finger, stylus, etc.). But can someone use an application without interacting with it directly? What if it is open and updating with new items that the user reads as the show up on the screen? So, the commonality amongst all of these use cases for our application is that the user needs to see the application in order to use it.

Thinking about that a bit more generally, consider all of the tabs a user has open at any given moment. If only one is visible at a time, why do the rest need to do anything at all (aside from pending operations a user has queued up)? Shouldn't the visible tab be the sole active window?

So a more general question arises: if a user cannot see our application, does it need to run if at all?

For our application at least, this makes perfect sense. Why waste resources continuously polling and updating (especially with animations) if the user can't see it? Couldn't we fall back to less frequent polling with no special effects for updating the page? Seems reasonable with little to no user impact. In fact, since the user can't see the page, how would they know.

The only problem is how do we know if the user can see the page? Well, thanks to the Page Visibility API proposal, we can do this today (through browser prefixes at the moment). The API is available with a webkit prefix in Chrome 13+, a ms prefix in IE10+ and a moz prefix in FF10+.

For the example below, using the non-prefix version (which will be the standard) and only checking if the document is visible or not.
function pageVisibilityChanged() {
  if ( document.hidden ) {
    // Application is not visible to the user
    // Adjust polling rates and display update for inactive display mode
  }
  else {
    // Application is visible to the user
    // Adjust polling rates and display update for active display mode
  }
}

document.addEventListener( 'visibilitychange', pageVisibilityChanged, false );
That's it. Really. That's all you need to do to be a great application neighbor and also help reduce energy use by reducing unnecessary resource use by your application.

UPDATE: Here is a browser prefix version for those who want to play today with Chrome 13+, IE10+ and FF10+ and any browser that implements the standard.
var visibilityAPI = ( typeof document.hidden != 'undefined' && { 'hidden': 'hidden', 'visibilitychange': 'visibilitychange' } ) || ( typeof document.webkitHidden != 'undefined' && { 'hidden': 'webkitHidden', 'visibilitychange': 'webkitvisibilitychange' } ) || ( typeof document.mozHidden != 'undefined' && { 'hidden': 'mozHidden', 'visibilitychange': 'mozvisibilitychange' } ) || ( typeof document.msHidden != 'undefined' && { 'hidden': 'msHidden', 'visibilitychange': 'msvisibilitychange' } );

function pageVisibilityChanged() {
  if ( document[ visibilityAPI.hidden ] ) {
    // Application is not visible to the user
    // Adjust polling rates and display update for inactive display mode
  }
  else {
    // Application is visible to the user
    // Adjust polling rates and display update for active display mode
  }
}

document.addEventListener( visibilityAPI.visibilitychange, pageVisibilityChanged, false );