Blog

  • Getting super clean content reports in Google Analytics using GTM

    In Google Analytics the urls are case sensitive, therefore in our content reports /index.html will be different to /Index.html, and querystring values will make Google Analytics to think that even if it’s the same page it will recorded as a new one, /index.html?cache=off and /index.html?cache=on will be recorded as 2 different pageviews for example.

    The first problem its easily fixable with a lowercase filter within the views, but the querystring parameters it’s going to be a problem … I bet you’re saying that you can just add them to the Exclude URL Query Parameters list within your view configuration page and Yes! that’s right, but I’m pretty sure that you’re likely going to end having some marketing campaigns adding new parameters, or IT adding some parameters to get some funcionality switched on (like enabling some caching feature or whatever).

    So today, we’ll be using Google Tag Manager to solve this problem of having all our content reports fragmented due the unexpected querystring parameters in our pages. So let’s think about it, wouldnt be easier to identify the real parameters and getting ride of the rest that are not expected for the page functionality?, If you think about it, it’s likely a better way to do it, we can know which parameters will be used in our site, but we cannot think on unexpected ones.

    To achive this, we’re going to make use of just one single variable in Google Tag Manager, yeah that’s it, just one single Custom Javascript variable.

    We’ll just need to configure the paramList array on the code top, and add there all the querystring parameters that we want to keep. Any other parameter that is not listed in our array will be removed from the querystring value that is going to be recorded by Google Analytics

    function(){
      try{
        
            // We'll need to defined the QS values we want to keep in our reports         
            var paramsList = ["two","one","three"];
    
            // CrossBrowser inArray polyfill 
            if (!Array.prototype.indexOf) {  
                Array.prototype.indexOf = function (searchElement /*, fromIndex */ ) {  
                    "use strict";  
                    if (this == null) {  
                        throw new TypeError();  
                    }  
                    var t = Object(this);  
                    var len = t.length >>> 0;  
                    if (len === 0) {  
                        return -1;  
                    }  
                    var n = 0;  
                    if (arguments.length > 0) {  
                        n = Number(arguments[1]);  
                        if (n != n) { // shortcut for verifying if it's NaN  
                            n = 0;  
                        } else if (n != 0 && n != Infinity && n != -Infinity) {  
                            n = (n > 0 || -1) * Math.floor(Math.abs(n));  
                        }  
                    }  
                    if (n >= len) {  
                        return -1;  
                    }  
                    var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0);  
                    for (; k < len; k++) {  
                        if (k in t && t[k] === searchElement) {  
                            return k;  
                        }  
                    }  
                    return -1;  
                }  
            }  
            var qsParamsSanitizer= function(qs,permitted_parameters){
            var pairs = qs.slice(1).split('&');
            var result = {};
            pairs.forEach(function(pair) {
                pair = pair.split('=');
                result[pair[0]] = decodeURIComponent(pair[1] || '');
            });
    
            var qsParamsObject = JSON.parse(JSON.stringify(result));
            for (var p in qsParamsObject){
                if(permitted_parameters.indexOf(p)==-1)
                    delete qsParamsObject[p];
            }
            var rw_qs = '?' + 
                    Object.keys(qsParamsObject).map(function(key) {
                        return encodeURIComponent(key) + '=' +
                            encodeURIComponent(qsParamsObject[key]);
                    }).join('&');
            if(rw_qs=="?") rw_qs="";
            return rw_qs;
         }        
         return qsParamsSanitizer(document.location.search,paramsList);
        }catch(e){
           // let's let GA to use the current location.href if
           // for some reason our code fails.
           return undefined;
        }
    }

    Now, we only need to set our pageview tag “page” parameter so Google Analytics uses the new sanitized array instead of the one that it’s on the url.

    We’re done!. Let’s see how it works with a screenshot

    Now you just need to sit down, and wait some hours to start seeing your reports in a clean way and with no fragmentation. Happy analyzing!

  • Tricks to deal with customized Google Analytics and Google Tag Manager integrations

    In the past years, I worked on implementing Google Analytlics and Google Tag Manager in a lot of differences scenearios and almost any kind of integrations. So I’m gonna show you some code snippets that may help you integrating Google Tag Manager and Google Analytics into your libraries in the best way possible.

    Checking is Universal Analytics is available

    Most integrations I’ve seen does check for the ga object within the DOM before trying to push something to it. This is really nice, and a check that should be done ALWAYS, but Universal Analytics allows you to change the default name for the “ga” object, so under those situations our code may not work as we’re expecting.

    Luckly for us, Universal Analytics creates another variable, named window.GoogleAnalyticsObject, that will allow us to grab the current object name being used by Universal Analytics.

    So using the following code will allow us to check is Google Analytics is really available even if the current name has been changed:

    if(window.GoogleAnalyticsObject && typeof(window[GoogleAnalyticsObject])=="function"){ 
       // Code Here
    }

    Checking for current dataLayer object name for Google Tag Manager

    Google Tag Manager users the “dataLayer” name by default, but it may changed to use any other name.

    This piece of code will allow us to find the right dataLayer name dinamically:

    var gtm_datalayer_names=[];
    if(typeof(google_tag_manager)!="undefined")
        for(i in google_tag_manager){
            if(google_tag_manager[i].gtmDom==true){
                gtm_datalayer_names.push(i);        
            }        
    }

    The we could do something like:

    window[gtm_datalayer_names[0]].push({});

    We’ll even be able to send pushes to all the different dataLayer available on the current page:

    if(gtm_datalayer_names.length>0){
            var i = window.gtm_datalayer_names.length;
            while(i--){
               var dlName = gtm_datalayer_names[i];
               window[gtm_datalayer_names] = window[gtm_datalayer_names] || [];                 
               window[gtm_datalayer_names[i]].push({
                  'event': 'my-event-name',
                  'dataLayerName': gtm_datalayer_names[i]
               });                                           
            }    
    }
  • Debugging and monitoring GTM Variables for errors

    Google Tag Manager does not throw any error when the code in a variable fails. This is not bad per se as it would save us from having our sites failing if something is not coded as it should, but it will blind us agains some minor errors that may not be noticiables, for example if our Enhanced Ecommerce is failing for a certain product, or from some specific browser.

    Thanksfully we can use try{}catch(e){} to capture those errors :). And we could use it to send an event to GA so we can monitor and fix the errors easily, having also all the info about where did the error happened, which browsers, location, etc.

    The main problem of sending an event within a variable error is that we may end having dozens of duplicated events for each error as variables got executed several times and not just once . The following piece of code will loop thru all the dataLayer pushes to find out if the current error has been already pushed to avoid this problem.

    try {
        // JS Variable Code Goes Here
    } catch (e) {
        if (window.dataLayer.filter(function(obj) {
                return obj.errorMsg === e.message;
            }).length == 0) {
            window.dataLayer.push({
                'event': 'variable error',
                'errorMsg': e.message
            });
        }
    }

    Now just set an event that fires on the custom event named “variable error”, and that reads errorMsg variable value from the dataLayer.
    You can easily personalize this to your needs.

    Let’s see an small report for an Enhanced Ecommerce Implementation that is using Variables instead of the dataLayer values to populate the info:

    debug_gtm_var_01



    Those errors won’t prevent the hit to be sent, but ecommerce data will be missing on them.

    But this is not where it ends, now you can use Real Time reporting to monitor your recently published container and be able to fix things in almost no time:

    debug_gtm_var_02

    Update Dec-2018:  Thanks Timur for letting me know that now GTM is declaring the dataLayer variable locally. Added window. scope naming to the snippet. 

  • Tracking Optimizely redirect type experiments

    In my previous post I’ve talked about how to manage to use the native Optimizely tracking when using Google Tag Manager . Now we’re going further and we’ll learn how to track the experiment that are based on redirects in the best way possible.

    So, firstly you’ll need to enable the Optimizely and Google Analytics integration. Now here is the tricky part. If we’re sending users to an experiment page that it’s based on a redirect we’ll be likely going to lost the original referral info (if not using utm parameters).  As it’s a javascript redirection, the original referrer info won’t be kept.

    In case we’re lucky because optimizely will write a cookie with the original referral info, se we can read it later! :). That way we could override the referrer field within our pageview tag to keep the real attribution for those visits.

    We’ll need a 1st Party Cookie variable to read this cookie value:

    Then we’ll need to do use this variable in our pageview tag this way:

    There we go, now even if something lands on a redirect experiment, the original referral will be tracked in Google Analytics.

  • Enabling Optimizely native Google Analytics Integration within Google Tag Manager

    As you may already know, Optimizely has an in-built Google Analytics integration, which will allow us to track the experiments and variations being showed to a client using custom dimensions.

    For enabling them, you’ll need to put the following lines after the create command and the pageview one:

    window.optimizely = window.optimizely || [];
    window.optimizely.push("activateUniversalAnalytics");

    This is a bit tricky when using Google Tag Manager native tags, since it doesn’t allow you to execute anything after the tracker creation command.

    To achive this we’ll be using a little hack ( using a Custom HTML tag ), and the tag secuencing that was released some months ago in Google Tag Manager.

    Code
    <script>
     
      window['ga']=window['ga']||function(){
      (window['ga'].q=window['ga'].q||[]).push(arguments);
      if(arguments[0]=="create")
      {
        window.optimizely = window.optimizely || [];
        window.optimizely.push("activateUniversalAnalytics");
      }
    }
    </script>

    Now we’ll need to set a tracker name for our pageview tag, since Optimizely will try to set the custom dimension data to a tracker name and Google Tag Manager created a random tracker name for each fired Universal Analytics Tag.

    After this we’ll need to enable Google Analytics within our experiment and define to which custom dimension index we want to send the data:

  • Closing “Inside GTM” Blog, all content now hosted here!

    Two years ago I started “Inside GTM”, a blog focused on Google Tag Manager and other TMS tools along with @nikalytics, but we haven’t published anything since February, so we’ve agreed to close it 🙁 .

    After publishing the notice in Twitter, some people asked what was going to happen with the content already hosted there, so I’m moving all the content I wrote there in my own blog, this is in this blog.

    Luckily if you didn’t know about that blog, today’s is a good day for you, since you’ll discover 6 new cool posts about tracking things using Google Tag Manager.

    Here is the list of the posts:

    Hope you enjoy them.

  • #Tip – Finding Legacy GA code after migrating to Universal Analytics

    You may have already migrated your Google Analytics Implementation to Universal Analytics. It may happen that you still have some legacy ga snippet lying around in some pages without having noticed it, for example in some landing pages that are not belong the default CMS system/templates.

    But we can easily check this out just looking at Google Analytics data. It’s as easy as crossing your pageview/event reports with the “Data Source” dimension.

    This dimension will be only set when using Universal Analytics endpoint. The hits sent to __utm.gif will have that dimension set as “(not set)” while Universal Analytics hit will force that dimension to be “web”.

    So, that is!. It couldn’t be more easy, and I’d suggest to include this comprobation in your Universal Analytics migration checklist. 

    In almost no time, you’ll be aware of :

    • Pages with an old tracking code.
    • Pages that may be missing the new code (if we did not remove the old one, it’s right to think that we may not tag them with the new one).
    • Pages with duplicate codes (GA + UA)
  • Finding pages missing Google Tag Manager snippet within Google Tag Manager

    This time, we’ll be using Google Tag Manager itself, to find pages in our site that may be missing Google Tag Manager. Ok, it may sound strange, but was not able to find any other way to say it .

    Basically we’re going to use a custom html tag, to detect is the previous page had loaded Google Tag Manager code.

    To achive this we’ll be using a single Custom HTML tag ,a cookie and the onbeforeunload event.

    The following flow chart will show you the logic that we’re going to follow to detect the pages that are missing the Google Tag Manager snipper for our site.

    So we’ll create a new custom HTML tag (that will be fired for ALL pages default trigger), and then add the following code to it, remember to update the “hostName” variable value to your domain root:

    <script>
      (function(){
        // Change this to your root domain name
      var hostName = 'thyngster.com';
        // On before Unload 
      window.onbeforeunload = function(e) {
      	expire = new Date();
      	expire.setTime(new Date()*1+10*1000);
      	document.cookie = "__ttt="+escape(1) + ";expires="+expire.toGMTString()+ ";domain=."+hostName+";path=/";
       	};  
    
        // If user's coming from elsewhere but our domain, exit.  
        if(location.hostname.split('.')[location.hostname.split('.').length -2].length==2)
          var rootDomainName = location.hostname.split('.').slice(-3).join('.');
        else
          var rootDomainName = location.hostname.split('.').slice(-2).join('.');
        if(document.referrer.indexOf(rootDomainName)==-1)
          {return;}
    
    
        function isCookiePresent(name) {
          match = document.cookie.match(new RegExp(name + '=([^;]+)'));
          if (match) return true; else return null;
        }
    
        if(!isCookiePresent('_ttt')){
          dataLayer.push({
            'event':'missingtag',
            'referrer': document.referrer
          });
          document.cookie = '__ttt=; expires=Thu, 2 Aug 2001 20:47:11 UTC; domain=.'+hostName+';path=/';
        }  
      })();  
    </script>

    Ok, that’s all! 

    Now we could create a new Event tag that fires when the event equals to “missingtag” to send that data to Google Analytics.

    If you’re struggling with some hits being sent from hardcoded tags, I suggest you to take a look to this great tip from @fastbloke  for being able to find them, and you could look to this other old post

    If you’re sending the data to Google Analytics, remember to set the event to a nonInteractional hit!

    Any feedback, comment or improvement will be really appreciated.

  • Tracking the Hover Intent with Google Tag Manager

    At the moment, Google Tag Manager listeners are limited to Form Submits, Link Clicks and Clicks (for any DOM element). We are going to write a custom listener for hover intents by users. This means that we’re not only to do something when the user pass the mouse pointer over an element, but instead we’re waiting a certain time with the mouse over the element before submitting the action to GTM.

    For this we’re going to use the mouseenter and the mouseleave javascript events.

    The mouseenter event is fired when the mouse is moved over a DOM element, e mouseleave  in the other side is fired when the mouse leaves the DOM Element.

    So we are going to start a time, when the mouseenter event occurs, and then the mouseleave event to disable the previous time. This way, if the pointer is not over an element for the set time, nothing will be sent to Google Tag Manager.

    Instead of pushing our data as we usually do, this time we’ll do it the same way Google Tag Manager does with it’s built-in listeners, so we’ll be pushing a gtm.hover  event to the dataLayer that will look this way:

    We’ll have the current gtm.element  info, and the gtm.elementClasses , gtm.elementId as we have with the others listeners in Google Tag Manager to allow us to set our triggers.

    We’ll need to create a new tag, let’s call it for example : “TOOL – Hover Intents Listener” and let’s put the following code into that tag:

    // Tracking Top Links Hover Menus
      function trackHoverIntent(selector, time) {
        var timeoutId;
        var elements = window.document.querySelectorAll(selector);
        for (var i = 0; i < elements.length; i++) {
          elements[i].addEventListener('mouseenter', function(event) {
            var targetElement = event.target || event.srcElement;
            var classes = targetElement.className;
            var id = targetElement.id;
            if (!timeoutId) {
              timeoutId = window.setTimeout(function() {
                dataLayer.push({
                  'event': 'gtm.hover',
                  'gtm.element': targetElement,
                  'gtm.elementClasses': classes,
                  'gtm.elementId': id,
                  'gtm.elementHoverTime': time
                });
                timeoutId = null;
              }, time);
            }
          });
        }
    
        for (var i = 0; i < elements.length; i++) {
          elements[i].addEventListener('mouseleave', function() {
            if (timeoutId) {
              window.clearTimeout(timeoutId);
              timeoutId = null;
            }
          });
        }
      }

    #Tip: Remember to put the code above betweentags if you don’t wanna inject a bunch of code into your visible HTML code.

    Then we’ll need to add the listeners to the elements we want this way:

    trackHoverIntent('img.thumbnail', 1500);

    For example this code above will send a gtm.hover event everytime the visitor moves his mouse over an image with the class “thumbnail” for more than 1.5 seconds.

  • Tips for debugging your Google Tag Manager implementations

    I’m going to share some of the tips and tricks I’ve learnt (and that I use on my daily basics work) in the almost past 3 years since Google Tag Manager was released.

    Tip #1 – Grabbing a dataLayer value from the console

    If we want to grab the current value in the Google Tag Manager’s dataLayer, it offers a built-in get method for it. When GTM loads a global object is created named google_tag_manager, that has a key that is the current GTM Id value.

    google_tag_manager["GTM-XXXX"]

    And it the we can get a dataLayer value using dataLayer.get(‘value’)

    google_tag_manager["GTM-XXXX"].dataLayer.get('event')

    This will return the current value for the ‘event’ key, at the time we execute it. As all sites have a different GTM Id string, we could use it this way to it works on any site, as the Id will be grabbed dinamically:

    google_tag_manager[(Object.keys(google_tag_manager)[1])].dataLayer.get('event')

    Tip #2 – Using console.table for a fancy view of your objects

    Most common use of the “console” command is to print values into your console tab, but we can do some other nice things, like easily printing a full object in a table using the “console.table” command.

    For example for printing the whole dataLayer into a fancy table like this:

    console.table(dataLayer);

    But this has became a must for me, when trying to debug my Enhanced Ecommerce implementations, for example for the products impressions, cart content, or for the transactions. We could view with a quick look if all our data is there, and in the format we really want. It’s so easy to detect any misspelling on the key’s, or some wrong typed key in some of the pushes, or whatever.

    Combining it with the previous tip is even more helpful. Here is an example for checking the sent product items within an standard ecommerce push for Google Tag Manager:

    console.table(google_tag_manager[(Object.keys(google_tag_manager)[1])].dataLayer.get('transactionProducts'))

    Or here is goes an example to see all the product impression details sent within a category page using Enhanced Ecommerce:

    console.table(google_tag_manager[(Object.keys(google_tag_manager)[1])].dataLayer.get('ecommerce.impressions'))

    isn’t it nice, clear and helpful?

    Tip #3 – Printing the dataLayer in a plain text format

    Any browsers allows you to print your dataLayer just typing it into the console, but when you have a large pushes it’s a bit difficult to search for the data, or if we have a lot of pushes you may end going crazy trying to look for something.

    All browsers have the JSON object that can easy and help us debugging and checking our dataLayer. We can get a plain text version of our dataLayer using the following command:

    JSON.stringify(dataLayer,true,"\t")

    Tip #4 – Beautifying JavaScript code with Chrome

    Most of the actual scripts are minified to save space, and in some way to ofuscate the code. If you need to dig inside the pages code it’s difficult to find things if the code is ofuscasted/uglfied/minified, and till now I’ve been copying the JavaScript code, copying it on some online service like ( asdad ) and copying the returned code back to my editor, what takes a lot of time.

    Today by a lucky change, I’ve found that Chrome DevTools already has an in-built code beautifier feature!.

    beautifier

    Do you have any other trick/tip that makes your daily work easier?, Please share them.
    Did you find those tips are useful?, Let me know about it.