Wednesday, August 30, 2017

Loading Google Analytics after page load by appending script in head doesn't always work

Leave a Comment

In the EU, there's a cookie law that requires you to load third-party scripts after the user expresses consent, by clicking, scrolling or navigating for instance. So I load 3 scripts by executing a function that's called after document loads. This is the code:

enter image description here

The problem is that it doesn't always work, nor always fail. I see sessions and activity but I also know for a fact that there are visits that don't trigger the scripts because when I tested it myself on several other computers, not all activity was saved in analytics.

What should I fix in the function to make it work all the time?

8 Answers

Answers 1

$(document).ajaxComplete(function() {         var _gaq = _gaq || [];       _gaq.push(['_setAccount', 'UA-XXXXX-X']);       _gaq.push(['_trackPageview']);         var loadGoogleAnalytics = function(){          var ga = document.createElement('script');            ga.type = 'text/javascript';            ga.async = true;            ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';             var s = document.getElementsByTagName('script')[0];            s.parentNode.insertBefore(ga, s);         }     }); 

Answers 2

You can try use this function that works fine to me:

function loadGoogleAnalytics(trackingId) {                 var deferred = $q.defer();                  function loadScript() {                     var scriptId = 'google-analytics';                      if (document.getElementById(scriptId)) {                         deferred.resolve();                         return;                     }                      var s = document.createElement('script');                     s.id = scriptId;                     s.innerText = "(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', '" + trackingId + "', 'auto');";                      // most browsers                     s.onload = initGA();                     // IE 6 & 7                     s.onreadystatechange = function () {                         if (this.readyState == 'complete') {                             initGA();                         }                     }                      document.getElementsByTagName('head')[0].appendChild(s);                 }                  $window.initGA = function () {                     deferred.resolve();                 }                  loadScript();                  return deferred.promise;             } 

Answers 3

If you have a look at this post, you can modify it slightly to achieve what you want:

<script type="text/javascript">   var _gaq = _gaq || [];   _gaq.push(['_setAccount', 'UA-XXXXX-X']);   _gaq.push(['_trackPageview']);     var loadGoogleAnalytics = function(){      var ga = document.createElement('script');        ga.type = 'text/javascript';        ga.async = true;        ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';         var s = document.getElementsByTagName('script')[0];        s.parentNode.insertBefore(ga, s);     } </script> 

Then just call loadGoogleAnalytics() whenever the user agrees with your Cookie Usage display

Answers 4

Other response seems to be targeting ga.js which is legacy, whereas your question seems to be oriented towards analytics.js (current).

Studying the analytics.js snippet a little you can easily find an answer (below, formatted with prettier):

(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",   "https://www.google-analytics.com/analytics.js",   "ga" );  ga("create", "UA-XXXXX-Y", "auto"); ga("send", "pageview"); 

The trick is easy : Google creates a global var GoogleAnalyticsObject which is a string representing the name of the global GoogleAnalytics. You can see that by default it's "ga".

So, at loading you need to create two global vars : GoogleAnalyticsObject which is a string and window[GoogleAnalyticsObject] which is the function ( which just accumulates events, this function is replaced when the lib is loaded I guess ). Then when your user accepts the cookies you can load the lib and you're done :)

Answers 5

Could you try adding the following instead of a call to .innerHTML()

//...text above... TheAnalyticsScript.text = ['ga-script-string', 'fb-script.string','hotjar.string'].join('\n'); $('body').append(TheAnalyticsScript); 

Please let me know if that works.

Answers 6

Here's my pick on the problem. The solution simply patches the default scripts to return function which then adds the script tag only after the consent is granted. Have a look:

// for GoogleAnalytics var loadGA = (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();    // call this function after receiving consent   return function loadGA() {     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');  // for Facebook window.fbAsyncInit = function() {   FB.init({     appId: 'your-app-id',     autoLogAppEvents: true,     xfbml: true,     version: 'v2.10'   });   FB.AppEvents.logPageView(); };  var loadFB = (function(d, s, id) {   //- call this function after receiving consent   return function loadFB() {     var js, fjs = d.getElementsByTagName(s)[0];     if (d.getElementById(id)) {       return;     }     js = d.createElement(s);     js.id = id;     js.src = "//connect.facebook.net/en_US/sdk.js";     fjs.parentNode.insertBefore(js, fjs);   } }(document, 'script', 'facebook-jssdk'));  // for HotJar var loadHJ = (function(h, o, t, j, a, r) {   h.hj = h.hj || function() {     (h.hj.q = h.hj.q || []).push(arguments)   };   h._hjSettings = {     hjid: 1,     hjsv: 5   };    return function loadHJ() {     a = o.getElementsByTagName('head')[0];     r = o.createElement('script');     r.async = 1;     r.src = t + h._hjSettings.hjid + j + h._hjSettings.hjsv;     a.appendChild(r);   } })(window, document, '//static.hotjar.com/c/hotjar-', '.js?sv=');  // finally when you receive the consent function onConsentReceived() {   loadGA();   loadFB();   loadHJ(); }  //- meanwhile you can continue using ga(), hj() whatever here... 

Each of the loader can be in it's own script tag but make sure about the ordering of those tags.

Answers 7

Thinking about what your code does, this function:

  1. Creates a <script> element filled with strings
  2. Adds it to the <head>

Note that the <head> is afaik supposed to be loaded only at the initial load. Depending on the browser implementation etc it might be the case that some of your visitor's browsers ignore the <script> tag you add after the page load is completed.

If I understand when you want to do right you want:

  1. To load the page without the tracking scripts
  2. Show a consent modal/popup
  3. On 'agree' click trigger this function without a page reload

note: many EU websites reload the page after cookie consent because they add the <script> tags with tracking to the page using backend rendering. Often clicking 'I agree to cookies' they store a agreed: true type cookie that changes what webpage their server sends.

If you don't care about reloads:

  1. Set a cookie when the user agrees
  2. Reload the page
  3. Have your backend (php/node/ruby/whatever) add a script tag based on this cookie

If you do care

Change your function to run the tracking codes instead of adding them to the DOM.

Just look at the tracking codes and break them down into easy to read code. For example the Ga code is actually simply:

var ga = document.createElement('script') ga.type = 'text/javascript' ga.async = true ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js' var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s); 

And then

function addTrackers() {  // Execute the tracking things instead of adding them to the DOM  } 

Answers 8

Rather than appending the entire analytics script, you could have it included always but remove the following line:

ga("send", "pageview"); 

Then, when the user accepts the cookies.. fire the analytics function that sends the beacon using that same line above. Analytics does not actually send any data unless being told to specifically. That would be the most logical way of doing what you are attempting.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment