Instantly share code, notes, and snippets.

Embed
What would you like to do?
JavaScript for persisting dataLayer array and data model composition across pages
(function() {
// Set the timeout for when the dataLayer history should be purged. The default is 30 minutes.
// The timeout needs to be in milliseconds.
var timeout = 30*60*1000;
// Change dataLayerName only if you've defined another named for the dataLayer array in your
// GTM container snippet.
var dataLayerName = 'dataLayer';
// Don't change anything below.
// Initial settings
var initialLoad = true,
oldPush = window[dataLayerName].push;
// Method to copy items from dataLayer from before the GTM container snippet was loaded.
var backfillHistory = function() {
var tempHistory = [],
i = 0,
len = window[dataLayerName].length - 1;
for (; i < len; i++) {
tempHistory.push(window[dataLayerName][i]);
}
return tempHistory;
};
// Method to check if object is a plain object.
// From https://bit.ly/2A3Fuqe
var isPlainObject = function(value) {
if (!value || typeof value !== 'object' || // Nulls, dates, etc.
value.nodeType || // DOM nodes.
value === value.window) { // Window objects.
return false;
}
try {
if (value.constructor && !value.hasOwnProperty('constructor') &&
!value.constructor.prototype.hasOwnProperty('isPrototypeOf')) {
return false;
}
} catch (e) {
return false;
}
var key;
for (key in value) {}
return key === undefined || value.hasOwnProperty(key);
};
// Method to merge the stored data model and the history model together.
// From https://bit.ly/2FrPQWL
var mergeStates = function(storedModel, historyModel) {
for (var property in storedModel) {
if (storedModel.hasOwnProperty(property)) {
var storedProperty = storedModel[property];
if (Array.isArray(storedProperty)) {
if (!Array.isArray(historyModel[property])) historyModel[property] = [];
mergeStates(storedProperty, historyModel[property]);
} else if (isPlainObject(storedProperty)) {
if (!isPlainObject(historyModel[property])) historyModel[property] = {};
mergeStates(storedProperty, historyModel[property]);
} else {
historyModel[property] = storedProperty;
}
}
}
};
window[dataLayerName].push = function() {
try {
// Build the history array from local storage
window._dataLayerHistory = JSON.parse(
window.localStorage.getItem('_dataLayerHistory') ||
'{"timeout": null, "history": [], "model": {}}'
);
// Initial settings
var timeNow = new Date().getTime(),
states = [].slice.call(arguments, 0),
results = oldPush.apply(window[dataLayerName], states),
oDataLayer = window[dataLayerName],
dHistory = window._dataLayerHistory,
oDataModel = window.google_tag_manager[{{Container ID}}][dataLayerName].get({split: function() { return []; }});
// Method to reset the history array to the current page state only
dHistory.reset = function() {
dHistory.timeout = null;
dHistory.history = backfillHistory();
dHistory.model = {};
mergeStates(oDataModel, dHistory.model);
window.localStorage.setItem('_dataLayerHistory', JSON.stringify(dHistory));
};
// From https://bit.ly/2A2ZcCG
dHistory.model.get = function(key) {
var target = dModel;
var split = key.split('.');
for (var i = 0; i < split.length; i++) {
if (target[split[i]] === undefined) return undefined;
target = target[split[i]];
}
return target;
};
// Add history if this is the initialization event itself
if (initialLoad) {
dHistory.history = dHistory.history.concat(backfillHistory());
initialLoad = false;
}
// If timeout is reached, reset the history array
if (dHistory.hasOwnProperty('timeout') && dHistory.timeout < timeNow) {
dHistory.reset();
}
// Push latest item from dataLayer into the history array
dHistory.history.push(oDataLayer[oDataLayer.length-1]);
// Merge GTM's data model with the history model
mergeStates(oDataModel, dHistory.model);
// Update the timeout
dHistory.timeout = timeNow + timeout;
// Write the new history into localStorage
window.localStorage.setItem('_dataLayerHistory', JSON.stringify(dHistory));
return results;
} catch(e) {
console.log('Problem interacting with dataLayer history: ' + e);
var states = [].slice.call(arguments, 0),
results = oldPush.apply(window[dataLayerName], states);
return results;
}
};
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment