Google Tag Manager: Tag Priorities Vs Tag Sequencing


As most GTM (Google Tag Manager) users will agree, this is a much discussed yet confusing topic! The documentation on these topics is very concise and to be honest precise in describing what these two options do and what to expect, yet some of the side effects of these two options, combined with asynchronous nature of JavaScript are left out to be inferred by the users. And this is where much of the confusion seems to come from. Even the many blogs out there on this very topic barely touch this context.
Hence we are going to discuss this very thing today.

Tag Priority

Tag priority as described by the documentation is a number associated with a tag which identifies the order of firing the tag. Firing, not completion. Secondly, firing is itself an asynchronous process. If you consider all simple HTML tags, firing them meaning adding them to the HTML of the page, which is what the GTM script is responsible for. What tag priority means is that all the html elements will begin to be added in order identified by the Tag Priority but the GTM script will not wait for the elements added before to load and execute before adding next. (also it cannot know if it is finished, more on that later) And hence this does not govern the load order of the tags.

Tag Sequencing

Tag sequencing as described is a setting that governs which tags will fire before and after a particular tag. One can imagine this as a setup-run-cleanup processes, established with GTM tags (which is also apparent in the documentation). Think of it like a unit test; there is a setup which will fire before the tag (@Before in JUnit, or beforeEach in mocha), then the tag itself (@Test in JUnit or it("", ()=>{}) in mocha) and then the after-tag/clean-up tag (@After in JUnit or afterEach in mocha). If you were trying to make sure that a given tag fires before another, you should be happy sequencing exists.

Read the document carefully again and you will see it does not speak about completion yet again! Much like priority, sequencing cannot guarantee the completion of setup tag before firing the middle (test) tag! It will only ensure that setup tag is ‘fired completely’ before moving on to fire the middle / clean up tag.

In the Tag Priority documentation, it correctly states: “Tags will still be fired asynchronously (tags will fire whether or not the previous tag has finished.)” and “Tag Sequencing allows you to specify exactly which tags fire before and after a given tag.”. But says nothing of this sort in the documentation of Tag Sequencing.

By nature of JavaScript, it is difficult to know when execution of a particular snippet completes without explicit notification from the snippet; it can be in the form of an event being fired or a callback being triggered (Promise will come under this too); but as GTM does not ask for either, it cannot really know if your tag is ‘completed processing’.

We can see this by doing a simple experiment. In a GTM container, let us create 3 custom HTML tags as:

1. SetupTag:
<script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
  sayOnDoc("'jQuery' object defined on page: " + !!window.jQuery);
</script>

<script type="text/javascript">
  jQuery(document).ready(function() {
    sayOnDoc("'jQuery.ready' fired.");
  });
</script>
<script type="text/javascript">
  document.addEventListener('DOMContentLoaded', function() {
    sayOnDoc("'DOMContentLoaded' fired");
  });
</script>
<script type="text/javascript">
  var gtmName = 'google_tag_manager';
  var insideGTMAndDomReady = window[gtmName]
            && window[gtmName].dataLayer
            && window[gtmName].dataLayer.gtmDom;
  if (insideGTMAndDomReady) {
    sayOnDoc("'GTM ready' done.");
  }
</script>

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/3.16.5/math.min.js"></script>
<script type="text/javascript">
  sayOnDoc("'math' object defined on page: " + !!window.math);
</script>

<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.11.0/d3.min.js"></script>
<script type="text/javascript">
  sayOnDoc("'d3' object defined on page: " + !!window.d3);
</script>

<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.min.js"></script>
<script type="text/javascript" async=true src="https://cdnjs.cloudflare.com/ajax/libs/ag-grid/14.0.0/ag-grid.min.js"></script>
<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script type="text/javascript">
  sayOnDoc("'_' object defined on page: " + !!window._);
</script>
<script type="text/javascript" async src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.3.3/backbone-min.js"></script>

<script type="text/javascript">
  var someScr = document.createElement('script');
  someScr.onload = function(){
    sayOnDoc("Moment js added to page");
  };
  someScr.src = "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.19.1/moment.min.js";
  document.head.appendChild(someScr);
</script>

<script type="text/javascript">
  setTimeout(function() {
    sayOnDoc("My timer timed out..");
  }, 500);
</script>

2. MiddleTag:
<script type="text/javascript">
  sayOnDoc("Middle tag fired");
</script>

3. CleanUpTag:
<script type="text/javascript">
  sayOnDoc("CleanUp tag fired");
</script>
And create an html file or jsFiddle like this.
Now we simulate both scenarios:

Priority Test

Setup: Have all the three tags triggered on ‘All Pages’. Set priority of SetupTag set to 20, MiddleTag to 10 and leave CleanUpTag empty or zero.


Observation: The Setup tag is the first to fire. Mid way during the execution MiddleTag and CleanUpTag fire, while the Setup tag is still to complete.

Sequence Test

Setup: Have only MiddleTag triggered on ‘All Pages’. Set CleanUpTag as the clean up tag and SetUpTag as the setup tag for the MiddleTag.


Observation: The SetupTag starts to fire, all the script additions from the setup tag are done first, then the MiddleTag fires and finally the CleanUpTag, almost as if the next tag waits for the previous to complete. Yet the asynchronous sections of the SetupTag fire way after the CleanUpTag!

Additional Observations to Note

Note that in both cases the ‘async’ scripts cannot be guaranteed to be executed in order. Also that the event ‘DOMContentLoaded’ is completely skipped, this is because the event happens before GTM starts firing at all. Also interesting to note that although jQuery is loaded before, ‘jQuery.ready’ triggers after the ‘GTM ready’ is written to document, meaning that although jQuery is loaded on the page, there is a delay before the ‘ready’ event is fired; only that the delay is not long enough for text to show up after Middle/CleanUp tags. In both cases the completely asynchronous snippets: adding a script to page and a timeout delay happen way after the CleanUp tag.

There is a whole lot to discuss on the script loading and execution in browser, and asynchronous nature of JavaScript itself, which we have not and cannot cover in this post. There are a whole lot of different things that can happen depending on how we write the code in the tags and the browser you load the tags on. But at least we know that we cannot rely on GTM blindly to sequence the tags, especially if there are any asynchronous components in them.

As a side note, there are a whole lot of different and interesting scenarios that arise when we combine the priorities and sequencing with Tag firing options.

No comments:

Post a Comment

We need to talk, says one microservice to another

‘But how?’ asks the other service! Ever wondered how we communicate? One would not believe how complex and multi-step process it is. It in...