Page 1
Reaktor Mannerheimintie 2 00100, Helsinki Finland
tel: +358 9 4152 0200 www.reaktor.com [email protected]
Confidential ©2015 Reaktor All rights reserved
10 javascript concepts For advanced (web) analytics implementation
Simo Ahava Senior Data Advocate
Page 2
Simo AhavaSenior Data Advocate, Reaktor
Google Developer Expert, Google Analytics
Blogger, developer, www.simoahava.com
Twitter-er, @SimoAhava
Google+:er, +SimoAhava
Page 3
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
Get the basics right
Page 4
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
Get the basics rightGreat JavaScript learning resources
Page 5
http://www.codecademy.com/
Page 8
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
1. Functions
Page 9
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
1. Functions…multi-purpose, multi-use…
Page 10
function (number1, number2) { someGlobalProperty.set(number1 * number2); return number1 * number2;}
AVOID SIDE EFFECTS!
Page 11
function () { var timeNow = new Date(); return function() { return "Time at initialization was ": timeNow; }}
understand scope, utilize closures
Page 12
function() { var jsonLd = document.querySelector('script[type*="ld+json"]'); return jsonLd ? JSON.parse(jsonLd.innerHTML) : {};}
function() { return {{JSON-LD}}.author.name || undefined;}
Leverage return values for max flexibility!
Page 13
function() { return function() { window.dataLayer.push({ 'event' : 'commandComplete' }); };}
Modify state in closures, e.g. using hitCallback
Page 14
http://www.w3schools.com/js/js_function_closures.asp
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
http://www.simoahava.com/analytics/variable-guide-google-tag-manager/#6
further reading
Page 15
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
2. Data types
Page 16
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
2. Data types…stay loose…
Page 17
Number: 5String: "five"Boolean: trueArray: [1, 2, 3]Object: {name: "Simo"}Misc: undefined, null…
Page 18
var five = "5";five = 5;
dynamic type
Page 19
var five = "5";var ten = five * 2; // ten === 10
loose type
Page 20
typeof [1,2,3]; // "object"Array.isArray([1,2,3]); // truetypeof undefined; // "undefined"typeof null; // "object" undefined === null; // falseundefined == null; // truetypeof NaN; // "number"isNaN(NaN); // trueisNaN(null); // falseNaN === NaN; // false
weird stuff…
Page 21
window.dataLayer.push({ 'event' : 'GAEvent', 'eventData' : { 'cat' : 'Category Value', 'act' : 'Action Value', 'lab' : undefined, // PURGE 'val' : undefined // PURGE } });
use undefined to reset data layer variables
Page 22
http://www.w3schools.com/js/js_datatypes.asp
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures
http://www.simoahava.com/gtm-tips/undefined-dimensions-wont-get-sent/
further reading
Page 23
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
3. HTTP requests
Page 24
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
3. HTTP requests…loading resources without blocking the browser…
Page 25
the container snippet is a script loader
Page 26
it injects a script element into the dom
Page 27
which, in turn, downloads the gtm library
Page 28
<a href=url> <applet codebase=url> <area href=url> <base href=url> <blockquote cite=url> <body background=url> <del cite=url> <form action=url> <frame longdesc=url>, <frame src=url> <head profile=url> <iframe longdesc=url>, <iframe src=url> <img longdesc=url>, <img src=url>, <img usemap=url>
all these html elements invoke an http request
<input src=url>, <input usemap=url> <ins cite=url> <link href=url> <object classid=url>, <object codebase=url>, <object data=url>, <object usemap=url> <q cite=url> <script src=url> <audio src=url> <button formaction=url> <command icon=url> <embed src=url> <html manifest=url> <input formaction=url> <source src=url> <video poster=url>, <video src=url>
Page 29
GA does both get and post depending on payload size
Page 30
http://www.w3schools.com/ajax/ajax_xmlhttprequest_create.asp
http://www.w3schools.com/tags/ref_httpmethods.asp
https://developers.google.com/analytics/devguides/collection/protocol/v1/reference#transport
further reading
Page 31
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
4. Race conditions
Page 32
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
4. Race conditions…last one over the finish line is a failed request…
Page 33
async in the script tag means the resource is downloaded
asynchronously
Page 34
Synchronous: The web browser reads, requests, and executes from top-to-bottom, left-to-right.
Asynchronous: The web browser reads and requests from top-to-bottom, left-to-right. Execution depends on when the requests complete respectively.
Page 35
Synchronous: The web browser reads, requests, and executes from top-to-bottom, left-to-right.
Asynchronous: The web browser reads and requests from top-to-bottom, left-to-right. Execution depends on when the requests complete respectively.
Race condition: When the browser expects a proper sequence for executing commands, but this sequence cannot be guaranteed.
Page 37
var jQLoad = document.createElement('script'); jQLoad.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js'; jQLoad.addEventListener('load', function() { window.dataLayer.push({ 'event' : 'jQueryComplete' });});document.head.appendChild(jQLoad);
use callbacks to establish sequence
Page 38
<script> (function() { var el = document.createElement('script'); el.src = 'https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js'; el.addEventListener('load', function() { google_tag_manager[{{Container ID}}].onHtmlSuccess({{HTML ID}}); }); document.head.appendChild(el); })();</script>
tag sequencing can be used but it’s tricky
Page 39
http://callbackhell.com/
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
http://www.simoahava.com/analytics/understanding-tag-sequencing-in-google-tag-manager/
further reading
Page 40
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
5. History manipulation
Page 41
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
5. History manipulation…avoiding the dreaded page refresh…
Page 42
window.history.pushState( { pageType: 'formThankYou' }, 'Form Success', '/thank-you/');
pushstate creates a new history entry in the web browser
Page 43
window.location = '#thank-you';
changing location to #hash does the same thing
Page 44
window.history.replaceState( { pageType: 'formThankYou' }, 'Form Success', '/thank-you/');
replacestate replaces the current history state
Page 45
you can create triggers in gtm that react to these changes
Page 46
https://developer.mozilla.org/en-US/docs/Web/API/History_API
http://www.w3schools.com/js/js_window_history.asp
http://www.simoahava.com/analytics/google-tag-manager-history-listener/
further reading
Page 47
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
6. Browser storage
Page 48
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
6. Browser storage…introducing state to a stateless environment…
Page 49
function() { return function(name, value, ms, path, domain) { if (!name || !value) { return; } var d; var cpath = path ? '; path=' + path : ''; var cdomain = domain ? '; domain=' + domain : ''; var expires = ''; if (ms) { d = new Date(); d.setTime(d.getTime() + ms); expires = '; expires=' + d.toUTCString(); } document.cookie = name + "=" + value + expires + cpath + cdomain; }}
browser cookies are useful for simple storage
Page 50
{{Simo Cookie Solution}}('subscribe', 'true', 1800000, '/', 'simoahava.com');
browser cookies are useful for simple storage
Page 51
if (window['Storage']) { localStorage.setItem('subscribe', 'true'); sessionStorage.setItem('subscribe', 'true');} else { {{JS - setCookie}}('subscribe', 'true');}
// TO FETCHlocalStorage.getItem('subscribe');sessionStorage.getItem('subscribe');
HTML5 STORAGE IS MORE flexible BUT CAN BE DIFFICULT TO MANAGE
Page 52
http://www.w3schools.com/js/js_cookies.asp
https://developer.mozilla.org/en-US/docs/Web/API/Web_Storage_API
http://www.simoahava.com/analytics/two-ways-to-persist-data-via-google-tag-manager/
further reading
Page 53
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
7. DOM traversal
Page 54
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
7. DOM traversal…needles in haystacks…
Page 55
sometimes you need to retrieve an element without direct access
to it
Page 56
function() { return {{Click Element}} .parentElement .parentElement .parentElement .parentElement .parentElement; }
clumsy
Page 57
function() { var el = {{Click Element}}; while (el.className !== 'content-sidebar-wrap' && el.tagName !== 'BODY') { el = el.parentElement; } return el.tagName !== 'BODY' ? el : undefined; }
better
Page 58
http://www.w3schools.com/js/js_htmldom_navigation.asp
http://domenlightenment.com/
http://www.simoahava.com/analytics/node-relationships-gtm/
further reading
Page 59
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
8. CSS selectors
Page 60
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
8. CSS selectors…magnet for the needles…
Page 61
the most useful operator in gtm triggers
Page 62
css selectors let you identify elements on the page based on their
unique position in the dom
Page 63
http://www.w3schools.com/cssref/css_selectors.asp
http://www.simoahava.com/analytics/matches-css-selector-operator-in-gtm-triggers/
further reading
Page 64
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
9. jQuery
Page 65
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
9. jQuery…machine which sorts the magnets…
Page 66
function() { var el = {{Click Element}}; while (el.className !== 'content-sidebar-wrap' && el.tagName !== 'BODY') { el = el.parentElement; return el.tagName !== 'BODY' ? el : undefined; }
jQuery is excellent for abstracting many difficult issues
with working js in the browser
Page 67
function() { return jQuery({{Click Element}}).closest('.content-sidebar-wrap');}
jQuery is excellent for abstracting many difficult issues
with working js in the browser
Page 68
jQuery.post( 'http://www.simoahava.com/', // URL {subscriber: 'true'}, // Payload function() { window.dataLayer.push({'event' : 'requestComplete'}); } // Callback );
jQuery is excellent for abstracting many difficult issues
with working js in the browser
Page 69
you can load it in a custom html tag, but remember the race condition
Page 70
https://api.jquery.com/category/traversing/
http://api.jquery.com/
further reading
Page 71
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
10. dataLayer
Page 72
@SimoAhava from @ReaktorNow | #MeasureCamp | 10 Sep 2016
10. dataLayer…repository of semantic information - NOT just for GTM…
Page 73
datalayer is a global javascript array with a modified .push()
Page 74
Since it’s global, it’s easy to destroy
Page 75
it’s a message bus, and gtm processes the messages as they come,
and in sequence
Page 76
note that .push() is the only proprietary method. others
have no impact on gtm.
window.dataLayer.pop(); // does nothing in GTMwindow.dataLayer.shift(); // does nothing in GTMwindow.dataLayer.splice(); // does nothing in GTMwindow.dataLayer.slice(); // does nothing in GTMwindow.dataLayer.push(); // does lots of things in GTM
Page 77
https://github.com/google/data-layer-helper
http://www.simoahava.com/analytics/data-layer/
further reading
Page 78
[email protected]
www.simoahava.comTwitter: @SimoAhava
Google+: +SimoAhava