Blog

  • Pushing custom commands to Universal Analytics before Pageview tag execution

    Since I wrote the post about how to send a local copy of Universal Analytics hits I’ve been looking for a way to get this working within Google Tag Manager. But there’s no way to send anything between the tracker creating and the hit firing on GTM.

    Some time ago there was a discussion thread on Twitter commenting this problem:

    And even Simo Ahava wrote a post about it afterwards: http://www.simoahava.com/analytics/access-the-tracker-object-in-your-page-view-tag/

    Today I had some free time after lunch and I decided to get this working in my own way so and I’m showing a way to achive this in a pretty standard way, with multi-tracker support, with just one single HTML tag (affecting all hits sent by GTM) and still not needing to initialize the tracker within a Custom HTML tag, so you will just continue using the predefined Universal Analytics tags on Google Tag Manager. you remember I wrote about how Universal Analytics snippet works some time ago, and we’re going to use the stuff learned there on this post.
    Universal Analytics creates a global ‘ga’ object, and then a queue (‘q’ parameter) to process it later on(when analytics.js has been fully loaded) , this way:

    window['ga']=window['ga']||function(){
      (window['ga'].q=window['ga'].q||[]).push(arguments);
    }
    

    Google Tag Manager does nothing else but using the analytics.js API to track things then we’re going to hook our needed code right after the create command has been pushed to the queue:
    The following tag will us to set the current tracker clientID as the custom dimension 10:

    window['ga']=window['ga']||function(){
      (window['ga'].q=window['ga'].q||[]).push(arguments);
      if(arguments[0]=="create")
      {
      // Code that is going to be added to the queue after the create command
      ga(function() {
      tracker = ga.getAll()[ga.getAll().length-1];
      tracker.set('dimension10', tracker.get('clientId'));
      });
    }
    

    Yeah, that’s all , as easy as that, we won’t need to deal with dataLayer pushes, wait for something to be executed and better of all it will work for any Universal Analytics tags being fired from GTM.

    There’s just one more thing we will need to do, and it’s set the firing priority for this tag to a high value (100 or so) and firing trigger to “All Pages” so it gets executed as soon as possible and before any other tag  firing at “all pages” time by Google Tag Manager.

    tracker_create_hook_1

    Let’s take a look about how the queue would look without our code, and how it will end being after we hooked our code into it:

    tracker_create_hook_3


    When analytics.js starts to proccess the current commands queue, it will be process the new pushes we added right after the tracker has been created and before it sends the hit.

    This will not just allow to set custom dimensions on GA, we could use to work with the Universal Analytics Tasks , or we could use it to grab our testing values before Universal Pageview has been sent, or anything you ever wanted to do before the first hit was being fired and you never were able to control due to the asynchronously loaded tags by Google Tag Manager.
    To finish, let’s take a look to how would our tag look if we would like to send a local copy of universal hits:

    tracker_create_hook_4

    Find the code below:

    Thanks go to @AnalyticsNinja for helping me to test it.

  • JWPlayer video tracking using Google Tag Manager

    JWPlayer is one the most known video players (if not the most one) and today we’re going to learn how to track it using GTM (Google Tag Manager). This time we’re going to use an agnostic dataLayer pushes instead of the ones that we usually use for Google Analytics. 

    We’ll be using one tag, one trigger and one variable, and in the next list we’re resuming all the events that our code will be able to track:

    • Video Plays
    • Video Pauses
    • Video Completions
    • Video Progress (Percentages to be tracked can be easily defined in the first line of the code)
    • Video Mute and UnMute
    • Video FullScreen On and FullScreen Off
    • Video Errors

    As we said before we’re going to use a tool agnostic dataLayer pushes instead of focusing in the almost standard event tracking model from Google Analytics. The following object will be our video tracking data model:

    {
        "event": "video",
        "player_id": {{VIDEO_ID}},
        "interaction": {{VIDEO_ACTION}},
        "video_url": {{VIDEO_URL}},
        "duration": {{VIDEO_DURATION}},
        "width": {{PLAYER_WIDTH}},
        "height": {{PLAYER_HEIGHT}},
        "position": {{VIDEO_CURRENT_POSITION}},
        "resolutions": {{AVAILABLE_VIDEO_QUALITIES}}, // an array
        "volume": {{PLAYER_VOLUME}},
        "player_type": {{PLAYER_TYPE}} // html5 or flash
    }
    

    Most important data is the event, the interacction and the video_url (as used of the most video tracking scripts), but this time we’ll be adding some extra to the dataLayer pushes to be able to track more details info if we want to.

    Let’s start creating our needed configuration on Google Tag Manager. First thing that we’re going to need is to have a variable that is going to tell us if there’s any jwplayer available in the current page. We don’t want our tracking code to be injected in the page if we don’t have any video on it, do we?.

    jwplayer_tracking_variable

    Now it’s time to configure the trigger that is going to fire our tracking tag, so we’ll be using the previously created variable, and a second condition to trigger the tracking tag after the DOMReady event:

    jwplayer_tracking_trigger

    Let’s configure the tracking tag, after the screenshoot you’ll find the full code so you can copy & paste it:

    jwplayer_tracking_tag

    Tag Code

    Now all the video interactions info will be pushes to the dataLayer so you’ll have all that info available to send your events to Google Analytics or any other tool you like.
    Any suggestion, new tracking idea, feedback will be welcome, just leave a comment.

  • Yet another way/try to stop referrals/events spam on Google Analytics

    The spam referrals problem in Google Analytics is turning into the new “not provided”. Almost everyone I follow has posted/retweeted/said something about it in the past month. I didn’t want to miss this oportunity to bring back some of the scripts I did in the past year for myself and share it with everyone. So in this post I’m going to try to address the Spam problem from another perspective that is not based on using referrals lists and filters.

    As almost all of you may know, this problem is related to the Google Analytics Measurement Protocol not using any authentication mechanism, and being Websites tracked client-side it’s almost imposible to hide any protection mechanism if we don’t force user’s to be logged in some way to some service, what is not possible as we’ll need some way to track anonymous users. They’re using the Measurement Protocol to spam us? ok, so let’s play that game too and use the Measurement Protocol to protect our data from being spammed =)

    Some years ago I wrote some PHP scripts to track the ecommerce transactions server-side to avoid any client-side problem like: users blocking GA, JavaScript errors forcing ga to fail, users network problems, etc.

    So I updated that code to work with Universal to achieve the following goals:

    • Our real UA Number will be totally hidden for others, ie: there will be no way for anyone to guess our real Property number.
    • We’ll attach some string to our hostname parameters (&dh), and we’ll use that to filter our new view to prevent any spammers looping around al UA-XXXXX-Y to send hits.
    • As we’re going to proxy all our ga hits through our server-side script, it may allow other people to directly load that php file, so we’re using PHP sessions to avoid anyone to query that file if isn’t there any open session for that user.

    For Google Analytics Classic or the first Google Analytics Measurement Protocol releases, tracking every hit from server-side was a problem because we weren’t able to send the real users ip addreses (missing all GEO location related data), or their user-agents (this was possible forcing the request user-agent header anyway). But since some months ago this is no longer a problem as we can now send those values as parameters within our hits payload.

    First thing we need to do is to tell Google Analytics to send a copy of the hits to our own PHP tracking file (the one that is going to take care of proxing the hits to the real UA), this can be achived using the “Tasks”, so our Universal Analytics tag will look similar to:

    ga('create', 'UA-XXXXXX-Y', 'auto');
    ga(function(tracker) {
      var originalSendHitTask = tracker.get('sendHitTask');
      tracker.set('sendHitTask', function(model) {
      var payLoad =model.get('hitPayload');
      originalSendHitTask(model);
      var i=new Image(1,1);
      i.src="/collect.php"+"?"+payLoad;
      i.onload=function() { return; }
      });
    });
    ga('send', 'pageview');
    

    If you are interested on knowing how this piece of code works, you can take a read to this other post (it’s in spanish sorry …)

    Now we’ll need to install our server-side tracking code. It just has 2 files. gaproxy.class.php and collect.php . Just take care of uploading the collect.php file to accesible path on your site and then matching the code above with the right path for the file.

    You can grab the files from the following GitHub Repo: https://github.com/thyngster/ga-proxy

    So now that we have everything on place, let’s configure everything. In first place, let’s configure your own variables in the class file, it should be pretty straightforward:

        // Configuration Start
        // Set your real Property Nmber where you want to redirect the data
        private $property_id = 'UA-AAAAAAA-B';
        // This will be attached to the hostname value, so we can then filter any hit not coming from this script
        private $filterHash = 'dontspamme';
        // set this to true, if you want to remove the last Ip's Octet
        private $anonymizeIp = true;
        // Configuration End
    

    Now we’ll need to add this code to all our pages (into the header), so it creates the session, that will be used to avoid the collect.php to be called directly.

    <?php
    include_once('gaproxy.class.php');
    $ga = new GaProxy();
    $ga->setupProxy();
    ?>

    We’re finish!, Now if collect.php is called directly without any previously loaded page, or by a script that doesn’t allow and keep cookies, the session will not be active and our file won’t be sending those requests to Google Analytics endpoint.

    Let’s resume what we did:

    1. We told analytics.js to fordward a copy of the hits to our local collect.php
    2. We added 3 lines of php into our pages, so a session is started and a tracking token is set.
    3. We’ve configured the gaproxy.class.php file with our real UA Number, plus we added a little hash to be able to filter the real hits to our property.

    Now we’ll need to configure 2 filters in our views:

    1. One excluding all hits where the field hostname does not include our filterHash.
    2. One replacing our filterHash by an empty string.
    spamfilter_1
    spamfilter_2

    As you may see in the code there’s a reserved function that will be used to check the requesting IP’s against blacklists, maybe using some throttling mechanism, checking the referrers against a blacklisted list that can be automatically updated,  etc. This is actually in progress as I need to think about the best way to achieve these feature, so any suggestion will be really welcomed. I’m planning to port it to other languages as Python or Ruby too, but I’d like to have a more polished PHP version before that.

    I know this workaround will not be accesible to everyone, or it may take some extra efforts to get it running but using a plain filter, but it has more benefits and you won’t need to keep your filters updated for each new spammer that starts to mess around with your account.

  • Handy dataLayer debugging tool for Chrome

    One of those posts that were laying around on my drafts and that I’ve decided to publish. I know there’re out there some good and well stablished tools that helps you to debug your Google Tag Manager implementation like the dataSlayer extension. But in the previous months I’ve been using a little JavaScript snippet that allows me to view in easy way the pushes that are being sent to Google Analytics in real-time in a nice way:

    This is how the debugging will looks like:

    tool

    It works for Chrome +36, and it’s based on a few lines of code, to start using it you’ll just need to paste those lines into your browser console, and you’ll start seeing any new dataLayer pushes that are sent for the current loaded page:

    Object.observe(dataLayer, function(c) {
        for(i=0;i<c.length;i++)
        {
    	if(c[i].type=="add")
    	{
                console.log("%cNew dataLayer Push\n", "color: blue; font-size:15px;");
    	    console.log(JSON.stringify(c[i].object[c[i].name], null, '\t'));
    	}
        }
    });
    

    You can grab the code too from this gitHub repository: https://github.com/thyngster/universalanalytics/blob/master/datalayer_monitor.js

    A nice way to have this little debugger at hand is to add a new bookmark in Chrome and set the URL to this piece of code. From then on, clicking on the bookmark will enable the debugging for the current loaded page

    javascript: (function() {
        Object.observe(dataLayer, function(c) {
            for (i = 0; i < c.length; i++) {
                if (c[i].type == "add") {
                    console.log("%cNew dataLayer Push\n", "color: blue; font-size:15px;");
                    console.log(JSON.stringify(c[i].object[c[i].name], null, '\t'));
                }
            }
        });
    })();
    
    bookmark

    Not the best debugging tool, but pretty handy, hope someone finds it useful at some point.

  • How to track an intranet or an hybrid app

    As we all know (do we?) Google Analytics uses cookies in order to work. This is, if for some reason the cookie can’t be set it won’t give any error but hits won’t be fired at all. A cookie is usually set for a FQHN (Fully Qualified Host Name). So if for example we’re going to track our intranet and we access it using an URL like : http://intranet/ Google Analytics is likely not going to work (it will depend on the browser that is accessing the page.

    There’s another situation where we may have problems using the official analytics.js. For example when using a different protocol than http/https, an example of this if when we load an html file from the local file system ( file://index.html ). You may think this is not a usual way to access a webpage, but it’s the way the most Hybrid Apps work.
    A native app for a mobile is usually build using some wellknown web technologies like: HTML5, JavaScript and CSS, and uses a browser engine (not the browser itself) to load the main content locally (using the file:// protocol), and here we’ll hit the second issue, Universal Analytics has an internal task named checkProtocolTask, it basically aborts the request if the current protocol does match http/https.

    Another example of devices using html files loaded from the local filesystem will be the SmartTV’s  for example.

    Fortunately Universal Analytics gives us the possibility to change those behaviours in order to manage those situations.

    Tracking a non FQHN domain name

    Even if it may work for some domains we better assure that it will run for all browsers, for this we need to set cookie’s domain to ‘none’ ,

    ga('create', 'UA-XXXXXXX-YYY','none'});
    

    or

    ga('create', 'UA-XXXXXXX-YYY', {'cookieDomain': 'none'});

    Tracking a locally loaded page

    In this case we’ll need to set the storage engine to none, and disabling the protocol check:

       ga('create', 'UA-123123123-123',{
          'storage': 'none', 
          'clientId':'{{UUIDv4}}'
      });
      ga('set', 'checkProtocolTask', null);
      ga('set', 'appName', 'MyApp Name');
      ga('set', 'appVersion', '1.2.2');
      ga('send', 'appview', {
            'screenName': 'My Home Screen'
      });
    

    Let’s take to the 2 important line in the code above;

    'storage': 'none'

    This line disables the default cookie storage system, and set’s it to none.

     'clientId':'{{UUIDv4}}'

    As we have disabled the storage engine, we’ll need to manually set the clientId (the cid parameter within our hits) , usually most devices offers a way to gan Unique Identifier, or we could grab the mac address and convert it to a UUIDv4 value 🙂

      ga('set', 'checkProtocolTask', null);

    This last line disables the protocol checking task, so the requests are not aborted because it doesn’t match the http/https protocols.

  • Learn how does Universal Analytics Cross Domain work

    Univeral Analytics tracking is based on cookies and on a randomly generated clientId. Yep, that’s it. Each time you visit a site using Google Analytics for the first time a new cookie is set in your browser with a randomly generated hash (clientId), that will be used by Google Analytics servers to identificate you in the subsecuent visits.

    Let’s see how the cookie looks:

    crossdomain_universal

    Any of those values can be modified using the JavaScript API, and they need to be set within the tracker creation. In a 99.99999% of cases you won’t need to deal with this, but it’s always good to know that we have the possibility to define our own vales. Let’s see how a default tag would work if we were setting it manually:

    ga('create', 'UA-XXXXXXX-YYY', {
      'cookieName': '_ga',
      'cookieDomain': 'thyngster.com',
      'cookieExpires': 60 * 60 * 24 * 730  // 2 years expiration date
    });>

    As we can see in the previous screenshot Google Analytics does not just use the randomly generated hash to track the user (device), as it only is a 10 digits number, so in a high traffic site or even if not having good luck it could end in generaeting clientIds collitions.  To reduce the chance of having a duplicated clientId under our property. To address this problem Google Analytics add the cookie creation timestamp value to the clientId value instead of only the random hash. If our “_ga” cookie value is: “GA1.2.2055842233.1422407281” , the clientId would be: “2055842233.1422407281” and not just “2055842233”.

    Going back to the crossdomain tracking,  Google Analytics uses the clientId (cid parameter on the hits) to identify the users (devices), and it will make sense to think that when the user goes to a different domain the same clientId should be used on the destination domain. In order to be able to have the same clientId in 2 different domains we’ll need to pass the clientId value to the  destination domain, that way analytics.js will be able to use that clientId value for the destination domain instead of generating a new one (remember that ga cookies uses 1st party cookies).

    The JavaScript tracking API does provide some in-build methods to grab the linker parameters, or even to “decorate” the links to the “external” domain we define.

    ga(function(tracker) {
    var linkerParams = tracker.get('linkerParam');
    });
    
    crossdomain_universal_2

    You can see that all info returned by the linker makes sense, but there’s a new value that is not in our origin domain. What it is?. It’s a value that Universal Analytics uses to assure the cookie data integrity and to prevent in some way to have our cookie info hijacked. Using the linker parameter into a domain name using Universal Analytics with the allow linker option enabled will force the cookie override with the cookie values specified in the url, and we don’t to have anything sharing a link to our site in some social network and therefore messing up all our data because all users coming from that link have the same clientId do we?

    In the previous Google Analytics versions, the only not randomly generated hash that was being used was the domainhash ( first value in the __utma cookie). It was generated by a pretty simple  algorithm (click here to see a PHP port for the domainhash algo) .

    This time Google Analytics want further and uses al algorithm that take some more variables in mind, and most of them are not fixed values as it was the hostname.

    The current checksum hash uses the following infos:
    • The current clientId value
    • The current browser user agent
    • The current browsers timezoneoffset ( the difference in minutes with the UTC time )
    • All the descriptions from the available plugins within the current user’s browser (taken from window.navigator.plugins)
    • Current year, day hour and minute

    Below you can see the values used to generate the checksum for the crossdomain linking on my Firefox Browser:

    crossdomain_universal_3
    So at this point we’ll have an array with all those values, Next step analytics.js takes it to join all those array values using a dot, forming the following string/hash:
    crossdomain_universal_4
    Universal Analytics uses then this hashing algorithm (taken from analytics.js code) to convert the previous string into a numeric value (yep, you guessed it right, the one that was in our linker parameters value)
        function La(a) {
            var b = 1,
                c = 0,
                d;
            if (a)
                for (b = 0, d = a[y] - 1; 0 <= d; d--) c = a.charCodeAt(d), b = (b << 6 & 268435455) + c + (c << 14), c = b & 266338304, b = 0 != c ? b ^ c >> 21 : b;
            return b
        };
    

    When implemeting a crossdomain tracking the generated hash on the destion domain NEEDS to match with the current one generated when creating the tracker with the clientId data in the URL If they don’t match the cookie value won’t be overrided, and therefore crossdomain linking will fail and the cookie value will be created with a newly random clientId.

    Those are the points we need to take in mind when thinking in the crossdomain tracking:

    • The linker value is only valid when using the same navigator version and using the same plugins (same browser needs to visit the origin and destination domain).
    • The crossdomain tracking won’t work if we land on the destination domain later than 2 minutes after the linker value has been generated. ie: the linker value has to be generated on the fly when the user clicks on the destination domain, if we update the links on the page load and an user clicks on the destionation domain after the default time limit it won’t work. According to Google’s documentation, there’s a grace period of 2 minutes for the linker value. And that’s why Universal Analytics provides the decorate function, this function generates the linker value when user clicks on a link or either when a form is submitted, without the need of coding any javascript listeners to update the links value in real time when the user clicks on them.
    • The clientId used when generating the hash needs to match the one on the linker parameters, so even if having a valid hash for the linking, changing the clientId values won’t be possible.
    • User’s may not change the computer’s timezone configured while going to the destination domain.

    This is how the crossdomain linking works on Universal Analytics and how Universal analytics tries to keep the cookie data integrity and prevent in some way the Universal Analytics cookie hijacking.

  • How does the Universal Analytics snippet work

      (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
      (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
      m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
      })(window,document,'script','//www.google-analytics.com/analytics.js','ga');
    
      ga('create', 'UA-123123123', 'auto');
      ga('send', 'pageview');
    
    

    The first thing that may attract your attention is the function parameters, they look like a real word. Let start talking about what an “isogram” is, according to the wikipedia:

    An isogram (also known as a “nonpattern word”) is a logological term for a word or phrase without a repeating letter.

    i,s,o,g,r,a,m are the variables names being used in the Universal Analytics Tracking Code, and we don’t want to pass a duplicate variable name to it. So actually this is just a pun from the Google guys since any other word with not repeated letters could have been used.

    You may wondering what values are being passed to the function, and to know that we’ll need to look to the bottom of the Universal Analytics Tracking Code:

    (function(i,s,o,g,r,a,m){ ... }
    )(window,document,'script','//www.google-analytics.com/analytics.js','ga');
    

    This means “window” variable is being passed as the “i” parameter, “document” as the “s” parameter and so on.

    At his point we could translate the Universal Analytics Tracking Code as:

          window['GoogleAnalyticsObject'] = 'ga';
          window['ga'] = window['ga'] || function() {
              (window['ga'].q = window['ga'].q || []).push(arguments)
          }, 
          window['ga'].l = 1 * new Date();
          a = document.createElement('script'),
          m = document.getElementsByTagName('script')[0];
          a.async = 1;
          a.src = '//www.google-analytics.com/analytics.js';
          m.parentNode.insertBefore(a, m)
    

    Let’s look to the code line by line to see what it does:

    window['GoogleAnalyticsObject'] = 'ga';

    It creates a new global variable named “GoogleAnalyticsObject” that holds the main Universal Analytics object name.

    window['ga'] = window['ga'] || function() {
              (window['ga'].q = window['ga'].q || []).push(arguments)
    }
    

    Here the main ga object is created. If it is already defined then nothing happens, otherwise it will be declared as a function.
    This function will create a temporary window[‘ga’].q array and then push all the arguments that we pass to the function to that temporary array ( ga(‘value’,value’); ). It’s done this way as it may  happen that the very first ga(); calls may come before UATC has been executed. It saves them in that array and after the code has been executed it will process them. From this point on, subsecuent calls will be directly processed.

    window['ga'].l = 1 * new Date();

    This sets a new object property with the current timestamp value. Yes, multipling a javaScript Date object by 1 returns the current Date variable timestamp value.

    a = document.createElement('script')

    A new a new script element is created and saved into a variable named ‘a’ . This is what what will be injected into the page’s DOM, but not before some other things are set.

    m = document.getElementsByTagName('script')[0]

    Here the code looks for the first ‘script’ tag available on the DOM at the runtime, This will be used to attach the Universal Analytics tag before it.

    a.async = 1

    Sets the asynchronous attribute to our script tag, this will ensure the script will be run at the first opportunity after the script source is downloaded and before the window.load event without blocking the page parsing. Some browsers support the “defer” attribute which is the same as “async” but it will comply with the scripts order, again without blocking the page load.

    a.src = '//www.google-analytics.com/analytics.js'

    Now the src attribute is set for the script tag. This is from where the UATC is loaded. As you may have noticed the Schema is not specified, so it will be loaded from the current loading page schema, that is, if the page is loaded from https, the script will loaded from https://www.google-analytics.com and if the page is not under https, it will be loaded from http://www.google-analytics.com

    m.parentNode.insertBefore(a, m)

    Here is where the Universal Analytics Tracking Script is being injected into the page’s DOM, The first ‘script’ found on the page is being passed, looking for its parentNode, and then injecting the Tracking Code right before it.

    So this is basically how the UATC code works. I’m aware that my english on this post may not be the best, so any improvement over my explanations will be welcomed.

    Note: Simo-san just adviced me about this post that talks exactly about the same topic: http://code.stephenmorley.org/javascript/understanding-the-google-analytics-tracking-code/ , so you may want to check it too 🙂

    Thanks to Ranyere Rodrigues for helping me to improve the post with some english corrections 🙂

  • Pushing data to the right Google Tag Manager dataLayer namespace

    It may happen that the site we’re tracking is using a custom dataLayer name, or that it’s using more than one Google Tag Manager accounts at the same time and therefore (it should) using 2 different dataLayer namespaces.  Most of people just push the info to window.dataLayer variable, but it could be window.myOwnDataLayerNS . So we’d need to adapt our dataLayer pushes to that variable name.

    So we’re going to setup a Macro that is going to take care of returning the right dataLayer namespace name for our current container. This way we’ll not messing around with other’s containers namespaces and our scripts will work in any container even if we’re using a custom dataLayer name.

    First, we’ll need to enable the “Container IDbuilt-in Variable within our container:

    Now we’re adding a new variable, that will take care of reading the current “Container ID” and looking for it’s right dataLayer namespace. ( Remember, you’ll be able to find this macro code at the bottom of this post )

    So, we should have now a new variable name {{getDataLayerNameSpace}} available in Google Tag Manager, that would be allow us to send the pushes to the right dataLayer variable for our current container, without the need to taking care of the variable name defined on the gtm snippet.

    Then, we’ll be able to send the pushes to the dataLayer this way:

        window[{{getDataLayerNameSpace}}].push(
                                 {'event':'someEventName',
                                  'eventValue','Some Event Value'
                                 });
    

    Did you have found this post useful ?, then go ahead and leave us a comment 🙂

     Macro Source Code

    function() 
    {    
        var gtm_objects = [];    
        var gtm_counter = 0;    
        for (var i in window.google_tag_manager) 
        {
            if (typeof(google_tag_manager[i]) == "object" && i.match(/^GTM-/)) {
                gtm_objects.push({'accountId': i});
            }
            if (typeof(google_tag_manager[i]) == "object" && !i.match(/^GTM-/)) {
                gtm_objects[gtm_counter].dataLayer = i;
                gtm_counter++;
            }
        }
        for (var i in gtm_objects) 
           if (gtm_objects[i].accountId == "{{Container ID}}")
                return gtm_objects[i].dataLayer;
    }
    
  • Blocking your tags firing from your own IPs with Google Tag Manager

    The subject about how to exclude your own visits from GA has been commented before loads of times, most of time filters are used to keep out our own visits from Google Analytics , but some other tools may not have the possibility to filter out our own visits. This is why we’re going to use Google Tag Manager to block all our tracking pixels, conversions pixels, custom tags, or whatever tag type we want.

    We’re going to lay on a Lookup Table type macro and the dataLayer to get this sorted out 🙂 . Firstly, as JavaScript is not able to get the user’s IP address, we’re going to push the current user’s ip address to the dataLayer. We could use some JS based services to get the user’s IP but for using it this way we’ll need to delay our tags ( as we need to wait to receive the data from some external service, which could be down, or not reachable from user’s connection. So we’ll need to pass the request to our developers or IT team to implement a new key into the default dataLayer.

    As our site is certainly using some kind of server-side language, this should be a problem at all 🙂 . Let’s see an example about how could we do this using PHP :

    dl_optout

    Any other language will be able to do this

    Ruby

    request.env["REMOTE_ADDR"]

    ASP

    Request.ServerVariables("REMOTE_ADDR")

    PHP

    $_SERVER["REMOTE_ADDR"]

    Python

    os.environ['REMOTE_ADDR']

    As you see all languages give you access to the “REMOTE_ADDR” header variable, but this will fail if the user’s behind a proxy. So usually we’ll want to check for “HTTP_X_FORWARDED_FOR” and it’s not present we’ll use “REMOTE_ADDR“, this is up to the developers to print the visitor IP Address is the best way they can.

    Now, let’s setup 2 new macros, one of them will take care of reading our user_ip value from the dataLayer, the other will be a lookup table macro that will will use this first macro for the condition:

    Macro that grabs the visitor ip address from the dataLayer
    Macro that grabs the visitor ip address from the dataLayer
    Lookup Table that makes the magic happen
    Lookup Table that makes the magic happen

    Now we have 2 ways to go, either using a blocking rule to our tags (we’ll be using this if we want to block certain tags as we should now Blocking Rules prevail over Firing Rules ) , adding a new condition to our existing rules (if we want to block all tags depending on a generic rule, like “All Pages” :

    dl_optout_rule_all
    dl_optout_rule_notall

    There we go, now we could easily avoid sending data to Google Analytics from our Office IPs , but for any other tag that we’re using withn Google Tag Manager.

  • Tracking the browser orientation status and changes

    Last day we were talking about how to measure if our site was showing a responsive layout to our users, and today we’re going to expand that tracking post with orientation tracking for our sites.

    We could use the Media Queries to match the orientation, but as this is not much standard we’re going to use the window.orientation value and the viewPort Width and Height values to guess the current page orientation and pass that info to Google Analytics and track it using a custom dimension.

    orientation-tracking

    Actually those API’s and features are not support for much of the current browsers, so we’re going to write some extra code to rely on the viewPort width/height values if orientation values and event are not available within the current user’s browser.

    http://caniuse.com/#search=orientation
    http://caniuse.com/#search=orientation

    as

    <script>// <![CDATA[
    function getPageOrientation() { var orientation = window.orientation || window.screen.orientation; if(orientation) { // If the orientation is 0 or 180 page is in portrait mode, else landscape return (orientation == 0 || orientation == 180) ? "portrait" : "landscape"; }else{ // Let's rely on the viewPort values instead if orientation is no supporte by the browser var vpWidth= (Math.max(document.documentElement.clientWidth, window.innerWidth || 0)); var vpHeigth = (Math.max(document.documentElement.clientHeight, window.innerHeight || 0)); return vpHeigth>vpWidth ? "portrait" : "landscape";
      }
    }
    ga('set','dimensionYY',getPageOrientation());
    // ]]></script>

    As was mentioned before window.screen.orientation is a Experimental feature, therefore we’re going trying to use the orientationchange event if it’s available and if not we’ll be using to use the “resize” event that is widely supported by most browsers:

    <script>
    // Listen for orientation changes 
    // If present use orientation value and native event 
    var orientation = window.orientation || window.screen.orientation;  
    if(orientation)   
         window.addEventListener("orientationchange", function() {
             ga('send', 'Orientation Change', getPageOrientation(), null, null, true);
         }, false); 
    // If not, let's use resize event
    } else {
         window.addEventListener("resize", function() {
             ga('send', 'Orientation Change', getPageOrientation(), null, null, true);
         }, false);
     }
    </script>

    And this is how you could get some insights of what are most common orientations being used by your site visitors.

    Thanks fly to @SimoAhava for pointing me out to some code mess up I did while copying the codes into the post.