Intercept and Rewrite The Data Layer Push Events

Intercept and Rewrite The Data Layer Push Events

Lets consider a case when your client’s GA4 Configuration tag contains parameters that ought to be reset after the specific events. You can be also presented with other scenarios where you may want to intercept datalayer push events and drop properties, modify or add new ones. The task is not trivial and rather rare. Here is the solution.

Normally, you would not want to mess with the built-in JavaScript methods. However, GTM library itself, redefines as to how Array.prototype.push() must behave. Obviously, the data layer object, which is a regular JavaScript array, becomes a queue with some core functionality, as described in the documentation of GTM. Geeks (which I am sure you are), may also be interested in diving into Google’s Data Layer Helper Library.

Step 1 - lets address the case

GA4 Config tag contains parameters that will be pushed on each event. Indeed, the event specific parameters must live in the GA4 event tags. However, there are cases when GA4 setup and zoned containers can be served as a generic template for multiple sites, or something of the similar sort. After X event with its properties is pushed to the data layer and properties are picked by the tag, we want to reset those parameters to prevent them being added in the next event. To drop a parameter from the GA4 tracking request, it is enough to set it to undefined. Now, onto how to line up that logic.

Step 2 - define presets

I am gonna walk you through the solution backwards, as if a push event was already evaluated. But lets first define the presets.

As mentioned, we want to reset only specific data layer properties, which are attached to the certain events. In this case, we will evaluate someEvent. Next, we create a reference to the data layer and create a copy of the original dataLayer push. The latter, is an important point.

On the reference object of the data layer, we start defining the new push method. Here, we jump straightaway to the end of the function. Assuming that the anticipated event (someEvent) was evaluated, we want to save its data for the next event in the pipeline.

(function() {
  var eventPatterns = ['someEvent',];
  var windowDataLayer = window.dataLayer;
  var dlPush = windowDataLayer['push']; 
  
  windowDataLayer['push'] = function() {

    ...
    
    // save specific event to reset props on the next push
    for (var i=0; i < eventPatterns.length; i++) {
      var re = new RegExp(eventPatterns[i]);
      var eventName = arguments[0] ? arguments[0].event : undefined;
      window._obsoletePushProps = re.test(eventName) ? arguments[0] : {};
    }
  }
})();

Lets address an elephant in the room - arguments. As you may guess, this a special Arguments object formed by the GTM library and pushed to the data layer.

alt text

The rest is quite straightforward - if the currently evaluated event matches the one from the eventPatterns, then save the belongings of the Arguments object in the global JavaScript variable.

Step 3 - evaluate and reset event properties

As the next event is incoming, check if there is an object with the obsolete properties saved from the previous event. If it is there, it will loop through each obsolete property, check that this property is not part of of the current event’s Arguments object (we do not want to reset newly pushed properties), and set it to undefined. As mentioned, the properties with undefined value will be dropped from the GA4 hit.

(function() {
  var eventPatterns = ['someEvent',];
  var windowDataLayer = window.dataLayer;
  var dlPush = windowDataLayer['push']; 
  
  windowDataLayer['push'] = function() {
    var obsoletePushProps = window._obsoletePushProps || {};
      for (var i in obsoletePushProps) {
        if ( !(i in arguments[0]) ) {
          arguments[0][i] = undefined;
        }
      }

    ...
    
    // save specific event to reset props on the next push
    for (var i=0; i < eventPatterns.length; i++) {
      var re = new RegExp(eventPatterns[i]);
      var eventName = arguments[0] ? arguments[0].event : undefined;
      window._obsoletePushProps = re.test(eventName) ? arguments[0] : {};
    }
  }
})();

As mentioned, Arguments is a special object and you want to preserve its integrity to keep the data layer working and GTM responding to the events. Therefore, only the properties in the inner object of Arguments, can be modified.

Step 4 - the core of the dataLayer push

We approached the culmination of the function. We reset the global object (obsoletePushProps) and lastly, use Function.prototype.apply to call the original push method of the data layer with passing modified arguments object. Thus, we created a ‘middleware’ to mutate a pushed event’s object on fly, before it follows the normal logic, also defined by the GTM library.

(function() {
  var eventPatterns = ['someEvent',];
  var windowDataLayer = window.dataLayer;
  var dlPush = windowDataLayer['push']; 
  
  windowDataLayer['push'] = function() {
    var obsoletePushProps = window._obsoletePushProps || {};
      for (var i in obsoletePushProps) {
        if ( !(i in arguments[0]) ) {
          arguments[0][i] = undefined;
        }
      }

    window._obsoletePushProps = null;
      
    dlPush.apply(dataLayer, arguments);
    
    // save specific event to reset props on the next push
    for (var i=0; i < eventPatterns.length; i++) {
      var re = new RegExp(eventPatterns[i]);
      var eventName = arguments[0] ? arguments[0].event : undefined;
      window._obsoletePushProps = re.test(eventName) ? arguments[0] : {};
    }
  }
})();
Trigger once, example of data layer Logs

It is crucial that the custom HTML tag with this script is fired once on the page load.

alt text As you can see in the example, prop1 was reset on the next event (click) and if it would be picked by the parameter in the GA4 Config tag, it would not be included in the tracking request.

This solution potentially saves you from tangling additional tags and triggers in the GTM container as well as provides a good basis to tackle some race conditions.