Use Page Visibility API With GTM

How to use the Page Visibility API in Google Tag Manager for improved accuracy of web analytics tracking.

The Page Visibility API for web browsers is pretty sweet. It lets you poll, using some new properties of the document object, whether or not the current page is visible to the user. Visibility is hidden if the page is not open in the current browser tab instance, or if the browser window has been minimized.

In this post, I’ll give an example of how features of the Page Visibility API could be used with Google Tag Manager. Do note, however, that browser support for the API pretty much excludes all IE versions older than 10 from the scope of this article.

In this example, we’ll set up some tags, rules, and macros which will help us avoid receiving pageviews for pages which have been opened in tabs but were never read. I don’t know about you, but having a pageview for a page which wasn’t viewed sounds counter-intuitive. The pageview will only get sent once the page first becomes visible.

The Visibility Listener

First, we’ll create a visibility listener. What it does is dispatch a browser event every time the visibility state of a page changes.

  • Create a new Custom JavaScript Macro {{visibility prefix}}

  • Add the following code within:

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

  if ('hidden' in document) {
    return '';
  }

  // Loop through each prefix to see if it is supported.
  for (var i = 0; i < prefixes.length; i++) {
    var testPrefix = prefixes[i] + 'Hidden';
    if (testPrefix in document) {
      return prefixes[i];
    }
  }

  return;
}
  • Create a new Custom JavaScript Macro {{visibility hidden}}

  • Add the following code within:

function() {
  switch ({{visibility prefix}}) {
    case '':
      return document['hidden'];
    case 'moz':
      return document['mozHidden'];
    case 'o':
      return document['oHidden'];
    case 'webkit':
      return document['webkitHidden'];
    default:
      return;
  }
}
  • Create a new Custom HTML Tag

  • Add the following code within:

<script>
  if (typeof {{visibility prefix}} !== 'undefined') {

    var visibilityEvent = {{visibility prefix}} + 'visibilitychange',
        hiddenState = {{visibility hidden}},
        visibilityChanged = function() {
          if (typeof hiddenState !== 'undefined') {
            dataLayer.push({
              'event' : 'visibilityChange'
            });
          }
        };
  
    // Attach visibility listener to document
    document.addEventListener(visibilityEvent, visibilityChanged, false);
  }
</script>
  • Set tag to fire upon {{event}} equals gtm.js

First we create a utility macro which returns the required browser prefix for setting up the listener and for testing visibility state. In the next macro, we retrieve the state of the document (true: document is hidden, false: document is visible).

Finally, in the tag we create a listener for changes in page visibility. If visibility changes, a dataLayer event is pushed.

We will utilize this data in the following example.

Block Pageview Until Page Is Visible

So the point here is to not send a pageview to Google Analytics until the page becomes visible. This applies to all your events as well, so you’ll need to add these same rules and functions (modified to match the original firing logic) to your event tags, or you might end up having sessions with only events and no pageviews. This is to be avoided.

Here’s the logic:

  1. When the pageview is first set to fire (usually {{event}} equals gtm.js), use page visibility as a blocking rule. If the page is hidden, do not fire the tag.

  2. When the page becomes visible, use the visibility event as a trigger for the tag.

  3. When the pageview has fired, use its hitCallback to prevent the visibility listener from triggering the tag again.

So, let’s get going.

  • Create a new Custom JavaScript Macro {{visibility callback}}

  • Add the following code within:

function() {
  return function() {
    if (typeof {{visibility prefix}} !== 'undefined') {
      var visibilityEvent = {{visibility prefix}} + 'visibilitychange';
      document.removeEventListener(visibilityEvent, visibilityChanged);
    }
  }
}
  • In your pageview tag, create a new rule with the following two conditions, and add it to the tag. Keep all the old rules in place as well!

    • {{event}} equals visibilityChange

    • {{visibility hidden}} equals false

  • In your pageview tag, add the following Blocking Rule

    • {{visibility hidden}} equals true
  • In your pageview tag, browse all the way down to Fields to Set, and add a new field

    • Field name: hitCallback

    • Value: {{visibility callback}}

And that should do it. This is a long chain of actions, but it follows the path described in the beginning of this section.

Conclusions

The Page Visibility API is pretty cool, but it’s first and foremost designed to save resources. Use it to stop a video from playing when the tab is not focused, or to stop an image carousel from proceeding.

With Google Tag Manager, you could do a number of things, such as:

  1. Block pageview for non-visible pages (explored in this post)

  2. Send visibility during pageview as a Custom Dimension (to track if the page is opened in a new tab or directly)

  3. Track visibility change as a GA event (to see if people focus on your content)

  4. Pause Timer Listener when page is hidden, and restart it when it becomes visible

And so on.