How can I dynamically set the tracking code for Google Universal Analytics while sticking to a CSP that disallows inline scripts?

I have a website solution that deploys to multiple URLs, each branded to a different client. The deployment process sets various client configuration data in the web.config file, database, etc. The same site code is used for all clients, but different configuration data means that the site looks and behaves differently for each client.

One of the things I want to set for each customer is the Google Universal Analytics tracking code . That's the code that appears in the script block that is added to each page ( UA-12345678-1 "in the example below):

<script>
  (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-12345678-1', 'auto');
  ga('send', 'pageview');

</script> 

      

I have the tracking code in the ViewBag, so if I was willing to place this script block directly on my HTML page (or in my layout), then inserting the tracking code would be trivially easy:

ga('create', '@ViewBag.GoogleTrackingCode', 'auto');

      

However, I am using a Content Security Policy (CSP) to block websites, and as part of that, I have disabled inline scripts: all scripts must be loaded from .js files.

So, I created a JavaScript file containing a modified version of the script block above, which I link from my HTML page:

(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', ga_token, 'auto');
ga('send', 'pageview');

      

My plan was to set the value of the variable ga_token

before running that bit of the script. I thought I would write the value of the variable somewhere in HTML (in the data- * attribute):

<div id="ga-token-data" data-ga-token="@ViewBag.GoogleAnalyticsToken">

      

... and use some JavaScript (in the .js file of course), extract this value:

$(document).ready(function () {
    ga_token = $("#ga-token-data").data("ga-token");
})

      

However, this proves the complexity due to the order of execution. According to google documentation, the script block should be placed just before the closing tag <\head>

on the HTML page, and even though there are some fancy things in there so that it doesn't try to do anything until the page is loaded that call ga('create', ...

with tracking code is actually executed right away. Hence, the value is ga_token

not set until it is used.

In theory, I could move my code that sets the value ga_token

just above the Google script block, but doing so I would execute that code before the DOM loads - in which case I might not be able to get the value I want. (And I certainly couldn't use jQuery, since I won't be loading the jQuery libraries until later).

Is there any other way to do this?

+3


source to share


2 answers


It is okay to read the DOM before it is fully loaded, node manipulation is risky. So your solution could be like this:

Generated HTML page

<form id="serverData">
    <input type="hidden" name="gaToken" value="@ViewBag.GoogleAnalyticsToken" />
</form>

      



Your modified analytics downloader

<script>
var ga_token = document.getElementById("serverData").gaToken.value;
(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', ga_token, 'auto');
ga('send', 'pageview');
</script>

      

+2


source


Another implementation without jQuery and without <form>

...

<html id="dashboard" data-ga_id="{{ settings.GOOGLE_ANALYTICS_ACCOUNT }}">

      



And then...

var ga_id = document.getElementById("push-dashboard").dataset.ga_id;

if (ga_id) {
    (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', ga_id, 'auto');
    ga('send', 'pageview');
}

      

0


source







All Articles