Fixing Double DataLayer Pushes on Ecommerce Events because of eventModel

Here’s a typical problem in Shopify and Woocommerce configurations: in Google Tag Manager, you notice double datalayer pushes of ecommerce events, specifically “view_item”, “add_to_cart”, and “purchase” events. This pollutes your data, and it’s caused by the simultaneous presence of one app or plugin pushing GA4 DataLayer events, and another one pushing Google Shopping DataLayer events.

Here’s how to fix it.

The double DataLayer pushes of ecommerce events

We have noticed this in many Shopify installations, but also often in Woocommerce. Let’s focus on the latter now, but the same approach applies to Shopify.

You navigate your preview mode of GTM and you see something like this on a product page (for the scope of this debug, we’re going to focus only on the view_item event).

duplicated view item event in gtm

When you inspect the duplicated view_item event, you notice these two different syntaxes.

GA4 Syntax
dataLayer.push({
  ecommerce: {
    currency: "EUR",
    value: 250,
    items: [
      {
        item_id: "XXX",
        item_name: "Product Name",
        sku: "XXX",
        price: 250,
        stocklevel: 9,
        stockstatus: "instock",
        google_business_vertical: "retail",
        item_category: "Category Name",
        id: "XXX"
      }
    ]
  },
  event: "view_item",
  gtm.uniqueEventId: 9
})

The other view_item event, has this syntax

Old Google Shopping Syntax
dataLayer.push({
  event: "view_item",
  eventModel: {
    ecomm_pagetype: "product",
    value: 250,
    items: [
      {
        id: "XXX",
        price: 250,
        google_business_vertical: "retail",
        name: "Product Name",
        category: "Category Name" +
                  "amp; Category Name amp" +
                  "; Category Name"
      }
    ]
  },
  gtm.uniqueEventId: 8
})

How to solve the double datalayer pushes of ecommerce hits

At this point you typically have three options.

Obviously option 3 offers the best solution, as you’ll be able to send data to all the relevant platforms in the right syntax, but also Option 2 is a viable option.

So here’s how to filter the double datalayer push of your ecommerce events for your GA4 Triggers.

Filtering your ecommerce tracking triggers

The goal is for your ecommerce events triggers to ignore the duplicated push that contains the syntax based on “eventModel”, while firing only when the syntax doesn’t contain “eventModel”.

The way to achieve this depends on your current Ecommerce Tracking. On our end, we have the following implementation for tracking ecommerce events.

Ecommerce Tags configuration example

Let’s say that these are all the ecommerce events that you want to track in GA4:

  • menu_click
  • view_promotion
  • view_category
  • select_promotion
  • view_item
  • view_item_list
  • select_item
  • add_to_cart
  • remove_from_cart
  • view_cart
  • begin_checkout
  • add_payment_info
  • add_shipping_info
  • purchase

It would be really messy to make one trigger per each event, so it’s likely you’ve configured your trigger this way:

Trigger Configuration: Custom Event

Event (with regex matching checked): view_promotion|view_category|select_promotion|view_item|view_item_list|select_item|add_to_cart|remove_from_cart|view_cart|begin_checkout|add_payment_info|add_shipping_info|purchase

Then, most likely, you have one GA4 Event tag that sends the triggering event with all the custom paramters using the DataLayer information, to GA4.

Filtering with a Boolean Javascript

The most obvious way to try and fix the problem, is to set a condition to the Ecommerce Trigger so that it will fire when it reads one of your events, but only in specific cases:

ecommerce tracking trigger with regex matching

You can use a custom javascript variable to look for the presence of the object “eventModel”; if it’s found, it should output “true”; if not found, it should output “false”. Here’s an example javascript:

JavaScript
function() {
    var isEventModelPresent = false;
    if (typeof dataLayer !== 'undefined') {
        var dataLayerString = JSON.stringify(dataLayer);
        if (dataLayerString.indexOf('eventModel') !== -1) {
            isEventModelPresent = true;
        }
    }
    return isEventModelPresent;
}

But, depending on your implementation and how soon the DataLayer is populated compared to when the variable runs the script, this approach might be unreliable. I will update this post in the future after further investigation.

Filtering with Data Layer Variable lookup

Another and tested approach is to look for the “undefined” value of the first key in the eventModel object. Infact, if Google Tag Manager finds the key, it will render the content of the key, but if not, it will return “undefined”. You can leverage this behavior to stop your trigger from firing.

Here’s how:

Step 1:

Create the variable “eventModel”.

accessing the eventModel object in datalayer

Step 2:

Create the Javascript that will read the content of eventModel:

JavaScript
function() {
  var eventModel = {{eventModel}};
  if (eventModel) {
    return eventModel;
  } else {
    return undefined;
  }
}

Step 3:

Now access the first level of your DataLayer push. In this case, it was “eventModel.ecomm_pagetype

event model reading trough gtm

Step 4:

Now that we can read the content of ecomm_pagetype, we can hunt down that “undefined” response when the eventModel object is not present at all.

preventing double datalayer pushes recognition of the trigger

Conclusions

As a result, your ecommerce events are going to fire the tag associating with this redesigned trigger, only when the syntax of the DataLayer is the standard one (or the one of your choice). Hope this helps.

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.