Skip to content

Instantly share code, notes, and snippets.

@iampatgrady
Last active October 3, 2018 04:46
Show Gist options
  • Save iampatgrady/32623b5facabbd56618c09edbef8ce8a to your computer and use it in GitHub Desktop.
Save iampatgrady/32623b5facabbd56618c09edbef8ce8a to your computer and use it in GitHub Desktop.
Google Analytics - Dynamic Form Field Tracking Data Layer Implementation
/*
* 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