Last active
October 3, 2018 04:46
-
-
Save iampatgrady/32623b5facabbd56618c09edbef8ce8a to your computer and use it in GitHub Desktop.
Google Analytics - Dynamic Form Field Tracking Data Layer Implementation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Analytics Pros, 2017 | |
*/ | |
(function(data_layer) { | |
var start_time = Date.now() // start timer on page load | |
// loop through all forms on the page, keep track of index with "i" | |
document.querySelectorAll("form").forEach(function(form,i) { | |
var position_index = 1, | |
click_index = 1; // position, click index iterator | |
// loop through all input, select and textarea in form (don't include hidden fields) | |
form.querySelectorAll("input:not([type='hidden']),select,textarea").forEach(function(element) { | |
var pos_index = position_index; // TODO:refactor | temporary index, form field iterator | |
// if first click, mark the time spent idle since page load | |
element.addEventListener("focus", function(tag) { | |
if (click_index === 1) { // if first click since page load | |
var position = pos_index; // set position index | |
data_layer.push(formatGtmEvent( | |
getEventAction(form,i), // action | |
"idle time", // label | |
start_time, // seconds from previous | |
click_index, // click index | |
position // position index | |
)); | |
start_time = Date.now(), // reset timer for next event | |
click_index++ // increment click index | |
} | |
}); | |
// add "change" listener to element, only fires when field is altered: | |
element.addEventListener("change", function(tag) { | |
var position = pos_index; // set position index | |
data_layer.push(formatGtmEvent( | |
getEventAction(form,i), // action | |
getEventLabel(tag), // label | |
start_time, // seconds from previous | |
click_index, // click index | |
position // position index | |
)), | |
start_time = Date.now(), // reset timer | |
click_index++ // increment click index | |
}); | |
position_index++ // increment position index | |
}); | |
// send form submit event to GA | |
form.addEventListener("submit", function(){ | |
var position = position_index; // set position index | |
data_layer.push(formatGtmEvent( | |
getEventAction(form,i), // action | |
"Form Submit Intent", // label | |
start_time, // seconds from previous | |
click_index, // click index | |
position // position index | |
)), click_index++ | |
}); | |
}); | |
// abstract dataLayer for constructor utility | |
function formatGtmEvent(act, lbl, strt, clk, posi) { | |
// form field tracking event model: | |
return { | |
"event": "form-field-change", // gtm trigger | |
"event-category": "Form Field Tracking", // ga category | |
"event-action": act || null, // ga action | |
"event-label": lbl || null, // ga label | |
"seconds-from-previous": getSecondsFromPrevious(strt) || 0, // ga custom metric | |
"click-index": leftPad(clk) || null, // ga custom dimension | |
"position-index": leftPad(posi) || null // ga custom dimension | |
} | |
} | |
// update logic to prioritize DOM markup | |
function getEventAction(form,index){ | |
return ( // some forms have an object in the name attribute | |
( typeof form.name !== 'object' ? form.name : form.name.name ) || | |
form.id || | |
form.action || | |
form.class || | |
"form["+index+"]" | |
) | |
} | |
// update logic to prioritize DOM markup | |
function getEventLabel(tag){ | |
return ( // First Name* (*: required) | |
tag.target.getAttribute("placeholder") || //uncommon | |
tag.target.name || //recommended | |
tag.target.id || //fallback | |
tag.target.type || //fallback | |
tag.target.getAttribute("ng-model") //angular/fallback | |
) + | |
( | |
tag.target.required || //recommended | |
tag.target.getAttribute("aria-required") || //uncommon | |
tag.target.getAttribute("data-val") //uncommon | |
? | |
"*" : "" // if required, append "*" | |
) | |
} | |
// utility functions: | |
function getSecondsFromPrevious(start_time) { | |
return (Date.now() - start_time)/1000 | |
} | |
function leftPad(n) { // leftpad 0 for alphanum sorting | |
return n > 9 ? "" + n : "0" + n; | |
} | |
})(window.dataLayer = window.dataLayer || []) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment