Instantiating & Using The Google Tag Manager DataLayer - Data Layer Best Practices Pt 1

March 21, 2016
By Dan Wilkerson

We’ve collected a series of technical best practices designed to help you successfully interoperate with the Google Tag Manager Data Layer. These best practices are designed to help eliminate some of the peskier and harder-to-debug issues we run into when working with clients. This part will discuss the proper way to instantiate and work with the dataLayer.

Always Use .push()

Google documentation frequently demonstrates populating values in the data layer by simple instantiation, e.g.:

<head>
<!-- Some other HTML -->
  <script type="text/javascript">  
    datalayer = [{
      'foo': 'bar',
      'baz': 'boo',
      'primeDirective': [1, 3, 5, 7, 11, 13, 15, ...],
      'ecommerce': {
        ...
      }
      // Etc, etc, etc...
    }];
  </script>
</head>
<body>
  <!-- 
    ******************
      The GTM Snippet 
    ******************
  -->
  ...
</body>

However, in certain circumstances, directly instantiating the dataLayer like this is dangerous. Why? If the above code were placed below the Google Tag Manager snippet on the page, something not-so-nice would happen, e.g.:

<head>
  <!-- Some other HTML -->
</head>
<body>
  <!-- 
    ******************
      The GTM Snippet 
    ******************
  -->
  ...
  <!-- Moved script tags to bottom for speed - Mr. WellMeaningDev, 10/16/15 -->
  <script type="text/javascript">  
    datalayer = [{
      'foo': 'bar',
      'baz': 'boo',
      'primeDirective': [1, 3, 5, 7, 11, 13, 15, ...],
      'ecommerce': {
        ...
      }
      // Etc, etc, etc...
    }];
  </script>
</body>

By moving the dataLayer = [{}] statement below our Google Tag Manager snippet, we actually destroy the true dataLayer. Any subsequent .push()‘s will seemingly have no effect, never appearing in our Debug Panel. And it would extremely hard to debug, since events prior to the overwrite will appear in Debug Mode, while events fired after will not.

The problem is that our initialization code doesn’t check if there’s already a variable named dataLayer. Because of this, it effectively overwrites whatever is already associated with the namespace dataLayer when it’s executed.

Making things worse, Google Tag Manager is still using the now-un-namespaced dataLayer, so if we .push() additional events into the new dataLayer, Google Tag Manager misses them completely. The Debug Panel is no help, either, as it still will show whatever events were caught up until that point. It’s only by manually polling dataLayer in the console that you’ll discover the issue.

wut

Proper dataLayer Instantiation

To fix this, copy the big G. In Google’s scripts and snippets, they frequently have to interact with globals that may or may not be ready and available when the code is executing. To get around this issue, they use the following pattern:

var someArray = window.someArray || [];
someArray.push(something);

Look familiar? You might remember this snippet of the Classic tracking code:

var _gaq = _gaq || [];
_gaq.push([ ... ])

This pattern is incredibly useful in JavaScript. Literally translated, it says “set the value of the variable named someArray to whatever is already named someArray, or, if someArray doesn’t exist yet, set it to an empty array”. This pattern lets us sprinkle in commands to various services throughout the code, to be executed when the service is ready to go.

Google isn’t alone in using this pattern, either; Facebook and many others employ the same strategy for managing asynchronous resource loading and command queueing and execution.

We take it one step further in our best practice; although the above method is good for 99.99% of the time, our instantiation syntax is 100% bulletproof. Whenever we’re interacting with the dataLayer, we use this syntax:

var dataLayer = window.dataLayer = window.dataLayer || [];
dataLayer.push({
  'foo': 'bar'
});

By using this syntax, you’ll always reference or instantiate the global dataLayer, and scope the variable dataLayer locally to prevent any funky hoisting or scope collisions.

The reason you should take this approach is simple: over time, other development teams will add their own dataLayer code, or shift your code around. If you use the dataLayer = [{}]; style of instantiation, you’ll end up with some hard-to-debug issues whenever this happens (and trust me, it will).

Using var dataLayer = window.dataLayer = window.dataLayer || []; dataLayer.push({ ... }); ensures you’ll never run the risk of these issues popping up. Making this the standard syntax also prevents two teams from accidentally overwriting/deleting another teams dataLayer values when they add their own code later on.

Check out Part Two (listed below) of this series, where we’ll discuss how to push values into the dataLayer within Google Tag Manager Custom HTML Tags.

What are your thoughts on dataLayer interaction? Have you developed another clever solution to this issue? Share with us in the comments below.

Part one of our Data Layer Best Practices series.