Last active
February 28, 2025 06:15
-
-
Save sahava/baee63650eed471a2d1eac5825037a05 to your computer and use it in GitHub Desktop.
JavaScript for persisting dataLayer array and data model composition across pages
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
(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 oldPush = window[dataLayerName].push, | |
containerId = {{Container ID}}; | |
// 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; | |
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; | |
} | |
} | |
} | |
}; | |
var getODataModel = function() { | |
return window.google_tag_manager[containerId].dataLayer.get({split: function() { return []; }}); | |
}; | |
// **Initialize upon first load** | |
// Build the history array from local storage | |
var dHistory = window._dataLayerHistory = JSON.parse( | |
window.localStorage.getItem('_dataLayerHistory') || '{}' | |
); | |
// Method to reset the history array to the current page state only | |
dHistory.reset = function() { | |
dHistory.timeout = new Date().getTime() + timeout; | |
dHistory.history = backfillHistory(); | |
for (var prop in dHistory.model) { | |
if (dHistory.model.hasOwnProperty(prop) && prop !== 'get') { | |
delete dHistory.model[prop]; | |
} | |
} | |
mergeStates(getODataModel(), dHistory.model); | |
window.localStorage.setItem('_dataLayerHistory', JSON.stringify(dHistory)); | |
}; | |
// If initial load | |
if (!dHistory.timeout) { | |
dHistory.timeout = new Date().getTime() + timeout; | |
dHistory.history = []; | |
dHistory.model = {}; | |
} | |
dHistory.history = dHistory.history.concat(backfillHistory()); | |
mergeStates(getODataModel(), dHistory.model); | |
// If timeout is reached, reset the history array | |
if (dHistory.hasOwnProperty('timeout') && dHistory.timeout < (new Date().getTime())) { | |
dHistory.reset(); | |
} | |
// From https://bit.ly/2A2ZcCG | |
dHistory.model.get = function(key) { | |
var target = dHistory.model; | |
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; | |
}; | |
// Write the new history into localStorage | |
window.localStorage.setItem('_dataLayerHistory', JSON.stringify(dHistory)); | |
window[dataLayerName].push = function() { | |
try { | |
// Initial settings | |
var states = [].slice.call(arguments, 0), | |
timeNow = new Date().getTime(), | |
results = oldPush.apply(window[dataLayerName], states), | |
oDataLayer = window[dataLayerName], | |
oDataModel = getODataModel(); | |
// If timeout is reached, reset the history array | |
if (dHistory.hasOwnProperty('timeout') && dHistory.timeout < (new Date().getTime())) { | |
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
Hi everyone,
Is there a possibility that if the website uses a pop-up form, the persistent data layer will not work? Because I can see the value of the variable before the page reloads. Once it reloads, the value is no longer there.