Google Tag Manager Single Page App Get Element On History Change

I have created a few variables for custom dimension on pageviews.

Pageview trigger: windowload or history change The data is being pushed through, but it's only getting previous pages' for example,

page/1 div class "page date" is 25th Jul, I would get undefined, but when I click onto page/2, i would get the page/1's "page date"

function() {
 return window.document.getElementsByClassName('page date')[0].innerText;


It seems that the history event is triggered before the corresponding page content is loaded into the DOM. This is nothing you can blame GTM for (GTM sees a history change, inspects the DOM, and grabs whatever it finds there, and that's the normal/expected behaviour).

Your solutions:

  • Make sure content is updated in DOM BEFORE the history event is triggered: this is something to sort out on the application side, and that may not be easily changed (if you use a framework like react it's probably best if you don't start hacking its core behaviour).

  • Delay the history event triggers: have a look at this solution which describes how to achieve this. Please note that solutions based on delays are never 100% reliable because there's a race condition between your delay and the loading of content, and you don't know for sure who will come first (and increasing the delay too much can cause the side effect of users changing pages in quick successions before analytics had a change to capture them).

  • Detect DOM changes: a more robust alternative would be to monitor the DOM for a particular element that is unique to each page (eg a <meta> element with the page ID or URL). You could use a tag to initiate the monitoring of this element when you receive the history change, and when the element actually changes it means the DOM has been updated, and you could fire your own trigger. This could be done via the MutationObserver or using a setInterval/setTimeout loop to check manually. However if the DOM is changed in several phases (blocks by blocks) this would not work (your <meta> element would have changed but not the div you're looking for), requiring you to start monitoring on a per-element or per-block level, which will be quite some work.

  • Push a dataLayer from your application: this would be my preferred option. I would hook into the logic of your application (you should be able to extend the routing method or the app framework should give you event listeners you can bind a custom function of yours with in which you can tell GTM that the page has been changed (eg dataLayer.push({'event': 'page_changed'});)