Why page visibility matters

Not every browser capability needs to be flashy. The Page Visibility API is a quiet but essential tool: it tells your application whether the page the user is looking at is actually visible on screen. That single piece of information makes it possible to build pages that adapt their behavior when nobody is watching them.

Common use cases include:

  • Slowing down server-polling or update cycles when the page is in a background tab.
  • Pausing image carousels, video, or audio until the user returns.
  • Only surfacing notifications when the page is hidden.

On mobile devices in particular, those adjustments translate directly into battery savings. A page that stops unnecessary work when hidden helps the user's device last longer between charges.

The API itself is modest: a pair of document properties for inspecting the current visibility state, plus an event you can listen to when that state changes. The specification is currently at the W3C Candidate Recommendation stage, and the core features are supported in current versions of all major browsers.

Checking the document state

The current spec defines two properties on the document: the boolean hidden and the enumeration visibilityState. The latter can currently take one of four values:

  • hidden: the document is not visible at all (minimized, on a background tab, or behind the OS lock screen).
  • visible: at least part of the document is visible on at least one display.
  • prerender: the document is loaded off-screen ahead of time. This value is optional, and not all browsers support it.
  • unloaded: the document is in the process of being unloaded. Also optional, and not universally supported.

One accessibility note: the spec allows hidden to report false even when a tool such as a screen magnifier is completely covering the document, as long as it is displaying a view of it.

Handling vendor prefixes

Older browsers, notably Android 4.4's browser, require vendor prefixes. A small helper isolates that detail so the rest of the code can use standard names, and can be dropped entirely once those browsers are out of your support matrix.

function getHiddenProp(){
    var prefixes = ['webkit','moz','ms','o'];

    // if 'hidden' is natively supported just return it
    if ('hidden' in document) return 'hidden';

    // otherwise loop over all the known prefixes until we find one
    for (var i = 0; i < prefixes.length; i++){
        if ((prefixes[i] + 'Hidden') in document)
            return prefixes[i] + 'Hidden';
    }

    // otherwise it's not supported
    return null;
}

With those helpers in place, a cross-browser check for visibility is straightforward:

function isHidden() {
    var prop = getHiddenProp();
    if (!prop) return false;

    return document[prop];
}

For finer detail, inspect visibilityState directly instead of the boolean. The four possible values are listed above, with the caveat that prerender and unloaded are optional per the spec and may not appear in every browser.

Reacting to changes

The Page Visibility API also defines a visibilitychange event, dispatched on the document whenever the visibility state flips. You register a listener just like any other document event:

// use the property name to generate the prefixed event name
var visProp = getHiddenProp();
if (visProp) {
  var evtname = visProp.replace(/[H|h]idden/,'') + 'visibilitychange';
  document.addEventListener(evtname, visChange);
}

function visChange() {
   var txtFld = document.getElementById('visChangeText');

   if (txtFld) {
      if (isHidden())
         txtFld.value += "Tab Hidden!\n";
      else
         txtFld.value += "Tab Visible!\n";
   }
}

A well-built web app respects the user's device and attention. The Page Visibility API provides a lightweight, standard way to do exactly that by letting your code pause, throttle, or defer work until the user is actually looking.