Engagement Timer Recipe for GTM

November 2, 2017 | Bounteous x Accolite

Adds engagement time tracking to every page on your site and sends events to Google Analytics.

By default, this tracks events every 15 seconds up to a maximum of 30 minutes, and pauses when the page is not the active tab in the browser or when the user hasn’t moved the mouse or pressed a key in the page in the last 65 seconds. (These values are configurable via variables.)



CU - Engagement Timer - LunaMetrics Plugin

GA - Event - Engagement Timer


Event - Engagement Timer


Engagement Timer Idle Milliseconds

Engagement Timer Interval Milliseconds

Engagement Timer Is Greater Than Limit

Engagement Timer Limit Seconds

Engagement Timer Seconds

Debug Mode


1. Download Container File

Download the container JSON file.

(You may need to right-click on the link and choose “Save Link As” or “Save Target As” to save the JSON file to your computer.)

2. Import JSON File into GTM

Log into your own Google Tag Manager container and head to the Admin section of the site. Under Container options, select Import Container. Check out this blog post for more details about importing a container file.

3. Update With Your Own Tracking ID

Update or create a new Constant Variable named {{YOUR_GA_TRACKING_ID}} with your Google Analytics Tracking ID (a.k.a. UA Number).

4. Preview & Publish

Use the Preview options to test this container on your own site. Try testing each of the events to make sure they’re working properly. If everything looks good, go ahead and publish!

Optional - Update Timer Interval and Limits

You can customize how often events fire by editing the variables:

Engagement Timer Interval Milliseconds

  • How often do we send events to Google?
  • (Default – 15000 milliseconds, or 15 seconds)

Engagement Timer Idle Milliseconds

  • How many seconds of inactivity until we pause?
  • (Default – 65000 milliseconds, or 65 seconds)

Engagement Timer Limit Seconds

  • What is the max amount of seconds we continue to send events?
  • (Default – 1800 seconds, or 30 minutes)


Time-on-page Engagement Timer Library & Google Tag Manager Plugin

Plug-and-play dependency-free engagement timer for observing user time on page engagement on the web. Supports everything north of IE9. Intervals can be set for every n seconds or at specific time milestones.

The timer can be set to go idle if the user stops triggering events in the targeted context. Any event that can be passed to context#addEventListener can be used. By default, the timer will not go idle.


Include the library in your code, then construct an EngagementTimer. Register a handler for emitted intervalevents by calling the EngagementTimer#on() method.

Include the library in your code, then construct an ScrollTrackerNOTE: It’s up to you to ensure the DOM is ready before constructing your tracker. Register handlers for pixels, percentages, or elements by calling .on() and passing in a configuration object:

Tracking w/ SPAs

To track scrolling with single-page applications (e.g. Angular apps), call EngagementTimer#reset() when a new “page” is rendered. This will reset the internal cache of marks that have been tracked and the time tracked so far. For example, using Angular 1.X.X & ngRoute:

// In your Config somewhere
$rootScope.$on('$routeChangeSuccess', function() {



Google Analytics

To track page engagement with Google Analytics, replace YOUR_GA_PROPERTY_ID with your UA number and adjust the opts variable to track what you’d like. Below is an example using all available configurations.

<!-- Default Google Analytics snippet somewhere up here -->
<!-- yadda yadda yadda -->
  (function(document, window) {

    var opts = {
      each: [5 * 60, 7 * 60],  // Track when we reach 5 minutes and 7 minutes
      every: [5],  // Track every 5 seconds
      startTime: new Date(),  // With the engagement time starting now
      idleAfter: 5,  // Go idle after 5 seconds unless
      engagementEvents: [  // one of these events fire in the context
      context: '#content',  // Set the context to be an element w/ id content
      idleOnVisibilityChange: true,  // Go idle when the user switches tabs
      max: 60 * 15,  // Stop tracking after 15 minutes
      min: 10  // Wait a minimum of 10 seconds before starting to track the time
    var trackerId = YOUR_GA_PROPERTY_ID;  // e.g. UA-000000-00

    getTracker(trackerId, registerEngagementTimer);

    function registerEngagementTimer(tracker) {

      var timer = window.EngagementTimer(opts);
      timer.on('interval', function(evt) {

        tracker.send('event', {
          eventCategory: 'Engagement Timing',
          eventAction: evt.data.time,
          eventLabel: document.location.pathname,
          nonInteraction: true



    function getTracker(trackerId, cb, ran) {

      var ga = window[window.GoogleAnalyticsObject] ;

      ga(function() {

        var trackers = ga.getAll();
        var len = trackers.length;
        var tracker;
        var i;

        for (i = 0; i < len; i++) {

          tracker = trackers[i];

          if (tracker.get('trackingId') === trackerId) return cb(tracker);


        if (!ran) {

          setTimeout(function() {
            getTracker(trackerId, cb, true);
          }, 0);




  })(document, window);

Google Tag Manager Plugin

Note: Google Tag Manager offers timers natively. Consider these first. If you need to monitor when a user is idle, use this library instead.

  1. Download the file ‘luna-engagement-timer.json‘ from the Github repository.
  2. In Google Tag Manager, navigate to the Admin tab.
  3. Under the Container column, select Import Container.
  4. Click Choose Container File and select the ‘luna-engagement-timer.json’ file you downloaded.
  5. Select Merge from the radio selector beneath the Choose Container File button.
  6. Select Rename from the radio selector that appears beneath the Merge selector.
  7. Click Continue, then Confirm.
  8. Navigate to the Tags interface – select the tag imported tag named GA Event – Scroll Tracking.
  9. Change the {{YOUR_GA_TRACKING_ID}} in the Tracking ID field to your Google Analytics Tracking ID (a.k.a. UA Number).

Once you publish your next container, engagement tracking will begin working immediately.

Technical Documentation


Constructs an EngagementTimer instance.

opts.each {number[]}

Track each time in seconds once. Either opts.each or opts.every must be set.

var contentTimer = EngagementTimer({
  each: [10, 25]  // Emits interval at 10 and 25 seconds.

opts.every {number[]}

Track every n seconds. Either opts.each or opts.every must be set.

var contentTimer = EngagementTimer({
  every: [10]  // Emits interval every 10 seconds


HTMLElement to treat as timing context. Defaults to document. Can be a CSS selector or HTMLElement.

var contentTimer = EngagementTimer({
  context: document.getElementById('content'),
  every: [10]

[opts.idleAfter] {number}

Number of seconds to wait before pausing engagement timer counter and entering ‘idle’ mode. A pause event will be emitted when the timer goes idle. Requires opts.engagementEvents to also be set. The idle countdown will reset whenever one of opts.engagementEvents is emitted by the context.

var contentTimer = EngagementTimer({
  every: [10],
  idleAfter: 5,
  engagementEvents: [

[opts.engagementEvents] {string[]}

Events that will prevent the timer from entering ‘idle’ mode if emitted by the context. A pause event will be emitted when the timer goes idle. Requires opts.engagementEvents to also be set. The number of seconds that can pass between each of these events is controlled by the opts.idleAfter setting.

var contentTimer = EngagementTimer({
  every: [10],
  idleAfter: 5,
  engagementEvents: [

[opts.startTime] {number} – UNIX timestamp

UNIX timestamp used to calculate the initial amount of time that has passed (defaults to when the script loads).

var contentTimer = EngagementTimer({
  every: [10],
  startTime: +new Date

[opts.idleOnVisibilityChange] {boolean}

Idles the tracker when the user switches their browser tab. Defaults to false.

var contentTimer = EngagementTimer({
  every: [10],
  idleOnVisibilityChange: true

[opts.max] {number}

A maximum number of seconds to emit intervals for. Once the max has been passed, EngagementTimer#destroy is automatically called.

var contentTimer = EngagementTimer({
  every: [10],
  max: 10 * 60  // Stops tracking after 10 minutes

[opts.min] {number}

A minimum number of seconds to record before emitting interval events.

var contentTimer = EngagementTimer({
  every: [10],
  min: 10  // Waits 10 seconds before starting to track

EngagementTimer#on(event, handler)

Registers a handler for a given event.

contentTimer.on('interval', function(evt) {




Resets the time tracked and start time. Emits an reset event.



Starts the counter. Emits a start event.



Stops the counter. Emits a pause event.


EngagementTimer#emit(name, data)

Emits an event and calls all handlers registered to the event. It is unlikely to be necessary to trigger an event manually.

contentTimer.emit('interval', {
  time: 10000


Destroys an EngagementTimer (unhooks the internal timers). The object will remain until garbage collected.