Timing issue with Datalayer to filter correct data

I’m having issues with waiting for the data needed within the DataLayer to become available for use later. At the moment I’ve got a setTimeout delay will help “solve” the problem but it’s obviously not a long-term solution since the DataLayer could take longer than my Timeout and I’d get the wrong data.

I’m working with an SPA which means I need to re-run my code when the page changes, and call the DataLayer again to then filter the data.

What I want to do is:

  • Call DataLayer on each page change
  • Filter through the data returned to find all instances of “view_item”
  • Look for the last “view_item” and print this out (for now - I’ll add further processing later)
setTimeout(function () {
    console.log("Initial dataLayer: " + JSON.stringify(dataLayer));

    // Update view_item with the latest dataLayer
    let view_item = dataLayer.filter(o => o.event === "view_item"); // Initialize view_item with initial dataLayer state

    // Log the length of the view_item array after filtering
    console.log("Length of view_item: " + view_item.length);
    // Log the items in view_item
    logItems(view_item);
}, 5000);

function logItems(item) {
    if (item) { // Check if item is defined
        item.forEach(item => {
            console.log("item: " + JSON.stringify(item));
        });
    } else {
        console.log("view_item is null or undefined.");
    }
}

Thanks in advance!

I see no code where you fetch the data layer. Is it an API call or a backend server call? What kind of backend are you using? Which language?

Hi @Shoxt3r, I don’t think the data layer is supposed to be read at all, it’s meant for pushing and can populate itself later with the usual

window.dataLayer = window.dataLayer || []

… but there’s no explicit way to wait for it AFAIK.

So if you need to know if a certain event has already been pushed, I’d suggest to create your own dedicated storage for this (e.g. in the session storage) and then push your events to both.

It’s about the GTM data layer BTW.

1 Like

Sorry - it comes through from window.dataLayer which is used by Google Tag Manager. It’s an object which has a range of events fed into it which I’m trying to pull together and then loop through to get the information needed. I don’t work on the backend of the website as that’s handled by a third-party so I’m unsure.

Thanks I’ll have a look into session storage. Basically, a number of events get pushed into the dataLayer as the user navigates around the website. I’m attempting to pick up on the “view_item” event, so I would imagine I’d want to check for any updates on the dataLayer with each new page view and then pick up on the “view_item” events to get the information I need. However, from what I’ve done so far, the call to dataLayer is happening too quickly and it only gets part of the data.

However, if there’s a better way of picking up on that same information then please let me know.

And yes, that’s correct - it’s coming through from Google Tag Manager.

Proxy objects crossed my mind. They enable you to add a handler for getting and setting. May not be doable, but just a thought.

1 Like

Well if the event isn’t getting pushed from your own application code, can you find out what exactly triggers the event and then basically track it yourself?

Or do you need to know what’s in the data layer specifically? Then maybe something like this (pursuing @rpg_digital’s idea)…

let dataLayer

Object.defineProperty(window, 'dataLayer', {
  get () {
    return dataLayer
  },

  set (value) {
    dataLayer = value
    // Do something
    console.log('data layer set')
  }
})

Edit: Okay this won’t get triggered when pushing new events. See below for a more sophisticated solution. :-)

2 Likes

Just a test using an array as a proxy and push

// takes a callback and returns a handler
const createHandler = (callback) => ({
    // will be invoked when a value is pushed to the proxy
    set(target, property, value) {
        if (target[property] === value) {
          return target?.length; // push should return the length
        }
        // Reflect.set hopefully returns true
        if (Reflect.set(...arguments)) {
          callback(...arguments)
          return true
        }
        return false
    }
});

// test callback function
const logArray = (obj, index, value) => {
  console.log(
    `${JSON.stringify(value)} pushed to index ${index}\n`,
    JSON.stringify(obj, null, 2)
  )
}
const sourceArray = [{ x: 1 }, { y: 2 }];
const proxy = new Proxy(sourceArray, createHandler(logArray));

setTimeout(() => proxy.push({z: 3}), 2000)
// after 2 seconds
/*
{"z":3} pushed to index 2
[
  {
    "x": 1
  },
  {
    "y": 2
  },
  {
    "z": 3
  }
]
*/

1 Like

Thanks a lot! I’ll give that a go. Presumably this could be setup to pick up on the dataLayer which is effectively an “external” array source? As I say data gets pushed in from the website remotely and I don’t have any control over when these pushes happen.

Hi @Shoxt3r ,

I’ve not worked with datalayers before, so I am not really qualified to give you a definitive answer. The idea of using a proxy was very much theory.

Gut feeling is that there has to be a better option, but it’s maybe an idea to do some tests with a proxy object if you can.