Author: David Vallejo

  • 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.

  • Tracking a responsive site and the media queries changes

    Every day more sites choose to use a responsive designs for their new sites, making it somekind difficult to track what version of our page is being showed to the user (as all we may now, browser detection is not perfect at all, most of them are based on the User Agent that can be easily faked).

    This post will try to show you the different ways to track if user’s are viewing a responsive layout or not, and using the Google Analytics Event Tracking to even track the layout changes if the browser window is resized by the user.

    New browsers allow you to query the current @media actually being used on the page using the matchMedia API though not all browsers have access to this API. The responsive designs work setting some new css’s rules based on the current page viewPort of the browser, this is; the usable width and height within the current browser window.

    As each site may have their own defined width limits to switch to a new layout, we’ll need to check out CSS files to find which one we’ll be using to detect if the user is viewing a different responsive layout but the desktop one.

    media_responsive

    It may happen that our site has more than one @media’s defined to make the site fully compatible with some specific devices ( ipad1, ipad2, etc ). If this is our case I recomend just to check for the main @media that really makes the difference, or if you want to go further you could even improve the current post examples to track every single available layour within your site.

    var matchMedia = window.matchMedia || window.msMatchMedia; var customVarResponsive; if(window.matchMedia) window.matchMedia("(max-width: 992px)").matches ? customVarResponsive='yes' : customVarResponsive='no'; else customVarResponsive = 'no'; ga('set','dimensionXX',customVarResponsive);

    As we’ve mentioned before not all browsers does support the mediaMatch API (You can check browsers support in the table below), so there’s some other more standard method to get this info, and instead of matching the current used @media, we’ll need to check the current viewPort width size againts the max-width defined in our CSS file:

    Source: http://caniuse.com/#feat=matchmedia
    Source: http://caniuse.com/#feat=matchmedia

    The following snippet will let you track the current browser’s viewPort and it’s fully compatible with all the browsers:

    if((Math.max(document.documentElement.clientWidth, window.innerWidth || 0))>=992)
      var customVarResponsive = "no";
    else
      var customVarResponsive = "yes";
    ga('set','dimensionXX',customVarResponsive);

    In any case using the matchMedia API is fancier way to achieve this, and is widely supported plus it give us more flexibility for example allowing us to use a listener to monitor the @media changes without needing to query the current viewPort size every X seconds, and why not sending an event if the user’s layout changed after the initial page load 🙂

    <script>
    // IE msMatchMedia var matchMedia = window.matchMedia || window.msMatchMedia; function getMaxWidth (mq) { if(mq.matches) // We do not want to send an interactive event ga('send','Responsive Tracking','Went Responsive',null.null,true); else ga('send','Responsive Tracking','Went Desktop',null.null,true); } if(matchMedia) responsiveCheck = window.matchMedia("(max-width: 992px)"); responsiveCheck.addListener(getMaxWidth);
    </script>
    
    
    
    
    

    There we have it, everytime the current user @media’s changes, a new event will be send to Google Analytics using the Event Tracking, or this can be extended to be user with any other tracking tool with not much efforts but calling our analytics tool method to do the event tracking, or setting the custom dimensions.

    Of couse this would allow us to have a different tracking for our sites, as we could define different event’s for each of our layouts.

  • Tracking fragments based navigation websites with GTM the right way

    We’re going to learn how to track those websites where the navigation is based on Ajax, ie: no pages reloads with Google Tag Manager.  As you may know a full page reload is needed in order to have Google Analytics tracking your pageviews, but those sites like the ones based on AngularJS are not reloading the page just the current page content, so this new pages won’t be tracked by our Analytics tools, or even catched by our conversion pixels.

    Those sites use the popstate event, to push a live URI change in our browser without reloading the page. We could write our own “popstate listener” to catch up those changes, but GTM is pretty smart and it has a tool called history listener , that we’ll use to track those uri changes.

    Every time that GTM detects a popstate event it will push an automatic event to the dataLayer called “gtm.historyChange” along with the previous fragment value and the new pushed one :

    event“gtm.historyChange”
    gtm.historyChangeSource
     “popstate”
    gtm.newHistoryState
     null
    gtm.newUrlFragment
     “contact”
    gtm.oldHistoryState
     null
    gtm.oldUrlFragment
     “”

    So basically what we’re going to do is adding a new pageview tag that is gonna be fired when this event is pushed to the dataLayer, but there’re some points that we need to have in mind:

    1. What happens if an user lands directly into a page with a hashtag, Google Analytics will just grab the current location.pathname + location.search
    2. Since Universal Analytics was launched, the referral behaviour changed, now it will overwrite the current visit attribution when a new referral is found while this domain names has not been added to referral exclusion list within our properties configuration section.

    Luckily we can think in some extra configurations in our Google Tag Manager container configuration to fix those previously mentioned situations.

    AngularJS

    Needed

    • 3 tags ( 1 will be fired when page is fully loaded, the second one will be fired based on the “gtm.historyChange” event and the last one will be the history listener tag type )
    • 2 firing rules ( First rule will allow us to fire a pageview web the user lands in our website the other will allow us to fire a virtual pageview when the uri changes without a page load )
    • 4 macros ( Here’s were we’re going to deal with the 2 problems we mentioned about before )

    Tags

    History Listener Tag

     Page Load Pageview Recording Tag

    Fragment Change Pageview Recording Tag

    Rules

    Rule for the default pageView Tag

    Rule for the fragment change pageView Tag

    Macros

    Macro for fixing the pagePath for page load Pageviews

    Macro for fixing the pagePath for Fragment Pageviews

    Null Value Returning Macro

    There we go, now our Google Tag Manager container will record the right pageviews taking in mind the fragment part when registering the pagePath, and it will take care of firing a new virtual pageView when the pages fragment is uptated. Just off the record, this will work too for implementation not only changing the fragment but for all page types that change the uri path in the fly. ( ie: /contact.php to /aboutus.php without a page reload )

    Any thoughs about this post ?, any improvement that you’ll have done to have a better tracking ?, Just leave a comment so we can discuss about it 🙂

  • Google using ISPs to cache Google Analytics endpoint

    I don’t really know if this is happening users using different ISP’s but starting from today I’ve noticed that all my requests to www.google-analytics.com were being served from a not usual but familiar IP address range, and the response time was just 9ms. Hey just a great improvement from the 42ms of average I’m usually getting from Google Analytics servers.

    thyngster@hq:~$ ping www.google-analytics.com
    PING www-google-analytics.l.google.com (212.142.160.238) 56(84) bytes of data.
    64 bytes from cache.google.com (212.142.160.238): icmp_seq=1 ttl=59 time=8.80 ms
    64 bytes from cache.google.com (212.142.160.238): icmp_seq=2 ttl=59 time=9.12 ms
    64 bytes from cache.google.com (212.142.160.238): icmp_seq=3 ttl=59 time=8.66 ms
    64 bytes from cache.google.com (212.142.160.238): icmp_seq=4 ttl=59 time=8.19 ms
    
    --- www-google-analytics.l.google.com ping statistics ---
    4 packets transmitted, 4 received, 0% packet loss, time 3004ms
    rtt min/avg/max/mdev = 8.190/8.698/9.129/0.344 ms

    That’s is, Google Analytics files are hits are being server locally from my ISP. It doesn’t seems to be a problem with my ISP, since it’s the Google Nameservers who are delegating that dns response:

    thyngster@hq:~$ dig +trace www.google-analytics.com
    
    ; <<>> DiG 9.9.5-3-Ubuntu <<>> +trace www.google-analytics.com
    ;; global options: +cmd
    .                       443274  IN      NS      b.root-servers.net.
    .                       443274  IN      NS      j.root-servers.net.
    .                       443274  IN      NS      e.root-servers.net.
    .                       443274  IN      NS      a.root-servers.net.
    .                       443274  IN      NS      m.root-servers.net.
    .                       443274  IN      NS      h.root-servers.net.
    .                       443274  IN      NS      i.root-servers.net.
    .                       443274  IN      NS      c.root-servers.net.
    .                       443274  IN      NS      d.root-servers.net.
    .                       443274  IN      NS      k.root-servers.net.
    .                       443274  IN      NS      l.root-servers.net.
    .                       443274  IN      NS      g.root-servers.net.
    .                       443274  IN      NS      f.root-servers.net.
    ;; Received 531 bytes from 212.142.144.66#53(212.142.144.66) in 295 ms
    
    com.                    172800  IN      NS      m.gtld-servers.net.
    com.                    172800  IN      NS      l.gtld-servers.net.
    com.                    172800  IN      NS      k.gtld-servers.net.
    com.                    172800  IN      NS      j.gtld-servers.net.
    com.                    172800  IN      NS      i.gtld-servers.net.
    com.                    172800  IN      NS      h.gtld-servers.net.
    com.                    172800  IN      NS      g.gtld-servers.net.
    com.                    172800  IN      NS      f.gtld-servers.net.
    com.                    172800  IN      NS      e.gtld-servers.net.
    com.                    172800  IN      NS      d.gtld-servers.net.
    com.                    172800  IN      NS      c.gtld-servers.net.
    com.                    172800  IN      NS      b.gtld-servers.net.
    com.                    172800  IN      NS      a.gtld-servers.net.
    com.                    86400   IN      DS      30909 8 2 E2D3C916F6DEEAC73294E8268FB5885044A833FC5459588F4A9184CF C41A5766
    com.                    86400   IN      RRSIG   DS 8 1 86400 20141130170000 20141123160000 22603 . KcH1dZuKA0dS9oDK2Wy8Iq/axbkR1/Dd0OnU3zgAUI88ym4rezyeHIJQ z6f7T2Ym2Ese+4ma1n0/9Q8iMvvTw8LAv+0ICzCuBtGQuCY3LhJ8uaRG AHoCVwv772zkalcRQuq07cxfllZmNykMyUgPcp/ViyQZtWcB/3eGFwJj mII=
    ;; Received 748 bytes from 198.41.0.4#53(a.root-servers.net) in 915 ms
    
    google-analytics.com.   172800  IN      NS      ns2.google.com.
    google-analytics.com.   172800  IN      NS      ns1.google.com.
    google-analytics.com.   172800  IN      NS      ns3.google.com.
    google-analytics.com.   172800  IN      NS      ns4.google.com.
    CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN NSEC3 1 1 0 - CK0QFMDQRCSRU0651QLVA1JQB21IF7UR NS SOA RRSIG DNSKEY NSEC3PARAM
    CK0POJMG874LJREF7EFN8430QVIT8BSM.com. 86400 IN RRSIG NSEC3 8 2 86400 20141129055344 20141122044344 48758 com. mKMCjqttB9SBFDa9+UYTil3/baYuVXdwSsrBjWybi05WCkGUvVBe14yF HrrQUMAue5E8qBwbESuqqhwgEdspaybxel8gCwFHFCAAl12Ok5VGtMCK gYlu3/nXwfFtN46WYMPU3k83n1j/UhSypjE6OajPWV/nKVzcKfHwYQ39 NM4=
    PMC1T49FLPALQ6HR7VPE0GQRFGSM38PS.com. 86400 IN NSEC3 1 1 0 - PMCAVG0VD8T2G2NPG5K6HV9F6T4VRMVT NS DS RRSIG
    PMC1T49FLPALQ6HR7VPE0GQRFGSM38PS.com. 86400 IN RRSIG NSEC3 8 2 86400 20141128052656 20141121041656 48758 com. cPUz3qVmzylXFxkLa870sFXMxAjAodtPcK3Nkoric83FKdaegulpxd9c eoY5zs3q5fKDNz8MjSkp4GO6MXsrbU2zOi9mueWmhVbb5OPAW7Od0DNg C6g+fSCRbIaOzsFHStWry9i7Rj9ZKPW8tayKQWuP2+p7FyTfATeg5IBA Jto=
    ;; Received 681 bytes from 192.41.162.30#53(l.gtld-servers.net) in 297 ms
    
    www.google-analytics.com. 86400 IN      CNAME   www-google-analytics.l.google.com.
    www-google-analytics.l.google.com. 300 IN A     212.142.160.238
    www-google-analytics.l.google.com. 300 IN A     212.142.160.223
    www-google-analytics.l.google.com. 300 IN A     212.142.160.240
    www-google-analytics.l.google.com. 300 IN A     212.142.160.229
    www-google-analytics.l.google.com. 300 IN A     212.142.160.234
    www-google-analytics.l.google.com. 300 IN A     212.142.160.230
    www-google-analytics.l.google.com. 300 IN A     212.142.160.251
    www-google-analytics.l.google.com. 300 IN A     212.142.160.212
    www-google-analytics.l.google.com. 300 IN A     212.142.160.218
    www-google-analytics.l.google.com. 300 IN A     212.142.160.216
    www-google-analytics.l.google.com. 300 IN A     212.142.160.208
    www-google-analytics.l.google.com. 300 IN A     212.142.160.249
    www-google-analytics.l.google.com. 300 IN A     212.142.160.241
    www-google-analytics.l.google.com. 300 IN A     212.142.160.227
    www-google-analytics.l.google.com. 300 IN A     212.142.160.245
    www-google-analytics.l.google.com. 300 IN A     212.142.160.219
    ;; Received 342 bytes from 216.239.32.10#53(ns1.google.com) in 42 ms
    

    So it seems that’s not really my isp who’s acting by it’s own serving me the ga.js/analytics.js and receiving my hits to the Google Analytics endpoint in some of their caching servers.

    ga_caching

    Actually even if a 304 Not Modified response code is being returned, hits are being registered in Google Analytics, so that shouldn’t be something about I should care about at the moment. I must add that ssl.google-analytics.com is not being cached for me at the moment:

    thyngster@hq:~$ host ssl.google-analytics.com
    ssl.google-analytics.com is an alias for ssl-google-analytics.l.google.com.
    ssl-google-analytics.l.google.com has address 74.125.230.62
    ssl-google-analytics.l.google.com has IPv6 address 2a00:1450:4003:807::101e
    

    I must say that this looks like a good thing for our Google Analytics implementations as hits will be sent faster by our user’s browsers, but in any case if you have any server-side implemention on Google Analytics using the Measurement Protocol, I’d add the “z” parameter to avoid any kind of malfunction on those cache servers preventing the hits being sent to real Google Analytics endpoint , even if that parameter is not obligatory ( it just needs to be a randomly generated number ).

    As I said I noticed this today, I’m not sure since when it has been up, or if Google is doing the same with any other ISPs . I think that they have some agreements with some isp’s to caché YouTube content too, saving bandwidth to Google and the ISP’s and giving to users a better experience while viewing videos.

    I tried with different browsers, different computers, different Operating systems and I’m experiencing the same behaviour for all of them, so I’m discarding some kind of configuration by me.

  • Protected: Keeping your Google Analytics clientId value as long as possible

    This content is password protected. To view it please enter your password below:

  • Google Analytics added sendBeacon functionality to Universal Analytics JavaScript API

    New  day and another not publicly published functionality (not that I was aware of) has been added by Google to Universal Analytics JavaScript API.

    Till now, for example if we wanted to track our outgoing links or our PDF downloads using an event or a virtual pageview  to be sure that the info was really sent to Google Analytics we needed to either write some javascript to delay the user redirection to the destination page or using the hitCallback functionality that was added to universal.js some months ago.

    This approach has a problem by itself, it will delay our user’s navigation, and if for some reason our delay function or hitCallback code was failing it will prevent our users to navigate to clicked link ( that’s not good, is it ? )

    So it seems that since lastest Google Chrome release ( v37 ) it’s supporting  the navigator.sendBeacon() method . This method can be used to asynchronously transfer small HTTP data from the user’s browser to the endpoint we want using a POST request.

    Browsers Support:

    Chrome : From v37
    Firefox (Gecko) : From version 31
    Internet Explorer: No supported ( This is really a surprise! )
    Opera: From version 24
    Safari: Not Supported

    Talking about Mobile Browsers, just Firefox Mobile 31.0 supports it.

    To use this feature, we will need to pass some extra object to our events this way:

    ga('send', 'event', 'Clicks', 'Outgoing', 'Reddit',  {useBeacon: true});
    

    When passing this value to our event, Universal Analytics will try to send our hit using this new feature on the browsers that support it, this way our user’s navigation won’t be affected and the event info will be asynchronously sent to Google Analytics even if the user left our page, or even if the user’s closed the browser window. Yep you heard it right, we could even track when user’s close our site’s tab.

    Writing navigator.sendBeacon will return undefined if it's not available in our browser

    Tracking window.unload will allow us to see how many users close their browser/tab while being in our site.  Let’s see how would we do it:

    window.addEventListener('unload', trackUnload, false);
    
    function trackUnload() {
       ga('send', 'event', 'Unload', location.pathname,  {useBeacon: true});
    }

    Let’s see a piece of pure JavaScript code to learn how this feature really works:

    window.addEventListener('unload', trackUnload, false);
    function logData() {
    navigator.sendBeacon("/log", JSON.stringify({'unload': location.pathname}));
    }
    

    This will send a POST request to /log path, with a POST payload inclusing the currentPathName were the user closed our browser window.

    I’m sure that there are lot’s of smart people out there that could think in some good use cases for this new feature ( even if it isn’t fully supported by all browser at the moment ).  Leave a comment if you have one in your head 🙂

  • Tracking HTML5 Videos with GTM

    Almost all new browsers support the video playing using HTML5 including mobiles and tablets, so it may be way to embed videos that can be considerated when publishing videos on our pages. HTML5 has support for most of the videos format used on internet, even YouTube has been running an HTML5 version of their site for a long. So we’re going to teach you how you can track those videos embed’s using the built-in API and Event Listeners. We have setup a working page example at: http://stage.tags.ninja/html5.php Insert an HTML5 video player on a page, is as simple as adding this code:

    <video controls="controls" width="320" height="240">
    <source src="my_video_name.mp4" type="video/mp4">
    </video>

    So let’s start tracking our videos step by step, what features is going to have this tracking:

    • Tracking of video percent played, the play button clicks, the pause button clicks, and video viewing
    • It will keep a track of what % buckets of the video have been already sent to save hits.
    • It will support multiple video embeds on the same page

    Tracking Flow

    1. Check if  there is any <video> tag in the current page. ( We don’t want to get the user’s browser executing this code if it’s not going to do anything )
    2. Wait till page’s DOM has been fully loaded ( gtm.dom )
    3. Fire the HTML5 Video Tracking Tag.

    Tags, Rules and Macros

    Tag

    html5_video_gtm_tag

    You can scroll down to see a fully comented code for this tag. The tag will have just 1 rule that will check for gtm.dom event from Google Tag Manager, and for the <video> tags presence that will be read using a Macro.

    Rule

    html5_video_gtm_rule

    Macro

    html5_video_gtm_macro

    Thanks flys to Eliyahu Gusovsky from Analytics Ninja from who I learnt the markers piece of code.

    Source Code

    <script>
    // Let's wrap everything inside a function so variables are not defined as globals 
    (function() {
        // This is gonna our percent buckets ( 10%-90% ) 
        var divisor = 10;
        // We're going to save our players status on this object. 
        var videos_status = {};
        // This is the funcion that is gonna handle the event sent by the player listeners 
        function eventHandler(e) {
            switch (e.type) {
                // This event type is sent everytime the player updated it's current time, 
                // We're using for the % of the video played. 
            case 'timeupdate':
                // Let's set the save the current player's video time in our status object 
                videos_status[e.target.id].current = Math.round(e.target.currentTime);
                // We just want to send the percent events once 
                var pct = Math.floor(100 * videos_status[e.target.id].current / e.target.duration);
                for (var j in videos_status[e.target.id]._progress_markers) {
                    if (pct >= j && j > videos_status[e.target.id].greatest_marker) {
                        videos_status[e.target.id].greatest_marker = j;
                    }
                }
                // current bucket hasn't been already sent to GA?, let's push it to GTM
                if (videos_status[e.target.id].greatest_marker && !videos_status[e.target.id]._progress_markers[videos_status[e.target.id].greatest_marker]) {
                    videos_status[e.target.id]._progress_markers[videos_status[e.target.id].greatest_marker] = true;
                    dataLayer.push({
                        'event': 'gaEvent',
                        'gaEventCategory': 'HTML5 Video',
                        'gaEventAction': 'Progress %' + videos_status[e.target.id].greatest_marker,
                        // We are using sanitizing the current video src string, and getting just the video name part
                        'gaEventLabel': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
                    });
                }
                break;
                // This event is fired when user's click on the play button
            case 'play':
                dataLayer.push({
                    'event': 'gaEvent',
                    'gaEventCategory': 'HTML5 Video',
                    'gaEventAction': 'Play',
                    'gaEventLabel': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
                });
                break;
                // This event is fied when user's click on the pause button
            case 'pause':
                dataLayer.push({
                    'event': 'gaEvent',
                    'gaEventCategory': 'HTML5 Video',
                    'gaEventAction': 'Pause',
                    'gaEventLabel': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1]),
                    'gaEventValue': videos_status[e.target.id].current
                });
                break;
                // If the user ends playing the video, an Finish video will be pushed ( This equals to % played = 100 )  
            case 'ended':
                dataLayer.push({
                    'event': 'gaEvent',
                    'gaEventCategory': 'HTML5 Video',
                    'gaEventAction': 'Finished',
                    'gaEventLabel': decodeURIComponent(e.target.currentSrc.split('/')[e.target.currentSrc.split('/').length - 1])
                });
                break;
            default:
                break;
            }
        }
        // We need to configure the listeners
        // Let's grab all the the "video" objects on the current page     
        var videos = document.getElementsByTagName('video');
        for (var i = 0; i < videos.length; i++) {
            // In order to have some id to reference in our status object, we are adding an id to the video objects
            // that don't have an id attribute. 
            var videoTagId;
            if (!videos[i].getAttribute('id')) {
                // Generate a random alphanumeric string to use is as the id
                videoTagId = 'html5_video_' + Math.random().toString(36).slice(2);
                videos[i].setAttribute('id', videoTagId);
            }// Current video has alredy a id attribute, then let's use it <img draggable="false" class="emoji" alt="?" src="https://s.w.org/images/core/emoji/2/svg/1f642.svg">
            else {
                videoTagId = videos[i].getAttribute('id');
            }
            // Video Status Object declaration  
            videos_status[videoTagId] = {};
            // We'll save the highest percent mark played by the user in the current video.
            videos_status[videoTagId].greatest_marker = 0;
            // Let's set the progress markers, so we can know afterwards which ones have been already sent.
            videos_status[videoTagId]._progress_markers = {};
            for (j = 0; j < 100; j++) {
                videos_status[videoTagId].progress_point = divisor * Math.floor(j / divisor);
                videos_status[videoTagId]._progress_markers[videos_status[videoTagId].progress_point] = false;
            }
            // On page DOM, all players currentTime is 0 
            videos_status[videoTagId].current = 0;
            // Now we're setting the event listeners. 
            videos[i].addEventListener("play", eventHandler, false);
            videos[i].addEventListener("pause", eventHandler, false);
            videos[i].addEventListener("ended", eventHandler, false);
            videos[i].addEventListener("timeupdate", eventHandler, false);
        }
    })();
    </script>

  • Tracking the content shared by users through AddThis with Google Tag Manager

    Today we’re going to learn how to track our AddThis Widget , to know what content of our site is being shared by our users. To do it, we’re going to use the eventListeners available from AddThis API and a Social tracking Tag.

    We’re going to use the same post format we used for our Tracking Disqus activity with Google Tag Manager post.

    Tracking Flow

    1. Check if  addthis has been loaded into the current page.
    2. Wait till page’s DOM has been fully loaded ( gtm.dom )
    3. Fire the AddThis Tracking Tag.

    Tags, Rules and Macros

    Addthis has a very useful API that will allow us to monitor and configure most of the options from the widget, one of them, is that it has Event Listeners for some of the users actions ( share, open, close ) that users can perform on the widget. We are going to use “share” event from Addthis to be able to launch a Social Tag based on the info proviced by the event. As we usually do, we are going to push the info into the dataLayer, so we can use that data afterwards to populate and fire your tracking tag.

    Tracking Tag

    We need to add a new Custom HTML tag, that will take care of setting up the AddThis listeners for when users shares something. ( The needed code is at the end on the post ).

    addthis_tag

    Rule

    We’re going to need two rules , one to fire our Addthis Tracking Tag, and it will have 2 conditions:

    1. Current page DOM must be loaded (gtm.dom)
    2. Current page must have the addthis global variable available. ( We’re using a Macro to gather this info. This Macro is defined in the next post section )
    addthis_rule

    And another one that will be used to fire the our Social Tracking Hit:

    social_rule

    Macros

    Lastly we’re going to configure all the macros needed for our tracking.

    We’ll need one JavaScript Variable Macro, this Macro will return the value of addthis variable ( window.addthis ) , if for any reason it isn’t available it will means that addthis has not been loaded on that current page therefore we will not fire our tracking tag.  It will return “undefined” as value if it is not available ( why whould we want to fire a tag that is not going to do anything ) .

    addthis_macro

    Secondly we will setup the needed  macros to read the values we pushed to the dataLayer when the user shared something . The following screenshot shows  the way the Macros have to be configured, the type for all of those one are “Data Layer Variable”:

    social_tag_macros

    Social Tracking Tag

    We’re almost done, we have a new tag that is taking the care of adding the event listener and pushing the info into the dataLayer, we have one Macro that will tell Google Tag Manager if Addthis is available on the page, 3 Macros that will read the the values for our Social Tracking  and finally we have a rule that will fire our tracking tag just when we need it.

    So we’re just missing 1 tag, that is gonna fire a Social Interaction hit to  Google Analytics.   (*Note: We are using a Social Tracking Hit in thous example, but could use and Event, or a virtual page view, you will have all the info pushed to the dataLayer, so just set the Macros and tags at your convenience).

    gaSocialTag

    Source Code:

    <script>
      function addThisEventHandler(evt) {
        switch (evt.type) {
            case "addthis.menu.share":
                // console.log(["_trackEvent", "Social Share", evt.data.service, evt.data.url]);
                dataLayer.push({
                    'event': 'gaSocial',
                    'gaSocialNetwork': evt.data.service,
                    'gaSocialAction': 'Share',
                    'gaSocialTarget': evt.data.url
                });
                break;
        }
    }
    // Let's set the events listener for addthis API 
    addthis.addEventListener('addthis.menu.share', addThisEventHandler);
    </script>
  • Trackeando lo que comparten nuestros usuarios a través de AddThis con Google Tag Manager

    Hoy vamos a aprender como hacer seguimiento de nuestro Widget de AddThis. Utilizando para ello los propios eventos que nos proporciona la herramienta y una etiqueta de Universal Analytics y tipo de tracking Social.

    Para explicaros como realizar la medición, vamos a continuar con el formato de post que ya utilizamos para aprender a Trackear los comentarios en Disqus with Google Tag Manager.

    Tracking Flow

    1. Comprobar que addthis está disponible en la página.
    2. Esperar a que el DOM de la página se haya cargado completamente ( gtm.dom )
    3. Lanzar el Tag de medición.

    Etiqueta, Reglas y Macros

    Addthis  dispone de un API muy completo que nos permite realizar muchas acciones, una de ellas es utilizar Event Listeners para algunas de las acciones que realice el usuario con el widget. Y esto es lo que vamos a utilizar para poder lanzar eventos sociales sobre Google Analytics. Como habitualmente hacemos, vamos a realizar un push al dataLayer para después mediante macros poder recoger esta información y lanzar nuestros tags.

    Etiqueta

    addthis_tag

    Reglas

    Tan solo vamos a utilizar una regla, que se va a encargar de lanzar nuestra etiqueta cuando sucedan dos cosas:

    1. Que el dom de la página haya sido completamente cargado (gtm.dom)
    2. Qué la página actual tenga el objeto de addthis disponible en el DOM . ( Para sacar esta información utilizaremos una Macro )
    addthis_rule

    Macros

    Y por último vamos a configurar una macro de tipo variable de JavaScript,  esta macro se encargará de devolver el valor de esta variable ( window.addthis ) , si por algún motivo no estuviese presente significará que Addthis no está disponible en la página. Por ello entonces su valor sera “undefined“, que es la condición que hemos utilizado en la regla que se encarga de disparar el Tag.

    Social Tracking Tag

    Pues ya tenemos casi todo lo necesario, tenemos un tag que se encargar de enviar la información al dataLayer y tenemos una regla y unas macros que se encargan de que este tag tan solo se lance cuando addthis está disponible. Ahora nos hace falta configurar un tag nuevo para enviar esta interacción social a Google Analytics.

    gaSocialTag

    Código Fuente: