I have a website solution that gets deployed to several different URLs, each of which is branded for a different client. The deployment process sets various client-specific configuration data in the web.config, database, etc. The same website code is used for all clients, but the different configuration data means that the site looks and behaves differently for each client.
One of the things that I want to set on a per-client basis is the Google Universal Analytics tracking code. That's the code that appears in the script block that gets 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 prepared to put this script block directly in my HTML page (or in my master layout), then inserting the tracking code would be trivially easy:
ga('create', '@ViewBag.GoogleTrackingCode', 'auto');
However, I'm using the Content Security Policy (CSP) feature to lock down the websites, and as part of that I've disabled inline scripts: all scripts must be loaded from .js files.
So, I've created a JavaScript file containing a modified version of the script block above, which I reference 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 ga_token
variable before this bit of script runs. I thought I'd stash the variable value somewhere in the HTML (in a data-* attribute):
<div id="ga-token-data" data-ga-token="@ViewBag.GoogleAnalyticsToken">
... and use some JavaScript (in a .js file of course) fetch that value:
$(document).ready(function () {
ga_token = $("#ga-token-data").data("ga-token");
})
However, that's proving tricky because of the order of execution. According to Google's documentation, the script block should be placed just before the closing <\head>
tag in the HTML page, and while there's some fancy stuff going on so that it doesn't attempt to actually do anything until the page is loaded, that ga('create', ...
call with the tracking code actually executes right away. Consequently, the value of ga_token
isn't set before it's used.
In theory I could move my code that sets the value of ga_token
to just above the Google script block, but in doing so I would be executing that code before the DOM was loaded - in which case I might not be able to fetch the value I need. (And I certainly wouldn't be able to use jQuery, since I don't load the jQuery libraries until later on).
Is there another way to do this?
It is OK to read DOM before is fully loaded, manipulation with nodes is risky. So your solution could be like this:
<form id="serverData">
<input type="hidden" name="gaToken" value="@ViewBag.GoogleAnalyticsToken" />
</form>
<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>