Event listener for dataLayer push events on Tag Manager JS

Viewed 5088

I want to add a custom script that integrates into an existing Tag Manager dataLayer on a page. I need this script to be notified about new dataLayer pushes. So whenever something on the page uses window.dataLayer.push, my script should be informed about this.

Is there a way to add a custom dataLayer event listener to the Google Script API?

I am looking for something like

google_tag_manager.dataLayer.onPush(callback);

google_tag_manager.dataLayer.subscribers seems to list how many dataLayer subscribers there are - but how can I add my own?

2 Answers

JavaScript has a Proxy object with which you can set traps whenever an operation is executed on an object. Arrays are also objects and can be used with proxies.

When calling the push method on an array, a new value will be set to the array. So setting a trap for the set method should allow us to add behavior to the push method.

In the trap, create a CustomEvent event and dispatch it. You can name this event anything you want. Whenever a value has been added to the dataLayer array, an event will be emitted. You can listen from every file to this event to act on whenever a value is added to the dataLayer array.

With Reflect we can make sure that the original behavior of the push method is restored while keeping the added behavior.

window.dataLayer = window.dataLayer || new Proxy([], {
  set: (obj, prop, value) => {  
    if (prop !== 'length') {
      const pushEvent = new CustomEvent('datalayerpush', {
        detail: value
      });

      window.dispatchEvent(pushEvent);
    }
    
    return Reflect.set(obj, prop, value);
  }
});

window.addEventListener('datalayerpush', event => {
  console.log(`Value pushed to dataLayer: ${JSON.stringify(event.detail, null, 2)}`);
});

window.dataLayer.push({
  event: 'test'
});

I'm not sure if you can do a 'native' implementation using the google_tag_manager object. You can, however, override the push method on the dataLayer array.

const originalPush = dataLayer.push
dataLayer.push = function(...args) {
    // notify here
    console.log('dataLayer.push was called');
    
    originalPush(...args);
}

You can then implement some logic based on the Observer Pattern.

Below I show a VERY simple implementation, just for demonstration.

const originalPush = dataLayer.push
const dataLayerListeners = [];
dataLayer.push = function(...args) {
    dataLayerListeners.forEach(listener => listener.notify())
    
    originalPush(...args);
}

class DatalayerListener {
    constructor(name) {
        this.name = name;
    }
    notify(args) {
        console.log("I'm listener "+this.name+" and I'm being notified that the dataLayer.push method was called");
    }
}

const listener1 = new DatalayerListener('listener1');
const listener2 = new DatalayerListener('listener2');

dataLayerListeners.push(listener1);
dataLayerListeners.push(listener2);
Related