Skip to main content

Turning classic GA calls into dataLayer pushes with GTM

Adam Englebright9 January 20204 min read
Turning classic GA calls into dataLayer pushes with GTM

This is part 3 of a semi-regular series, dedicated to reusable GTM solutions for scenarios or issues we’ve encountered more than once. Last time we looked at how to track clicks on iframes using GTM, and this time we’re looking at how to turn on-page classic Google Analytics calls to data layer pushes.

The problem with hard-coded GA calls

Say you still have hard-coded calls to classic GA on the page, but the library code itself was being delivered via GTM. It's now been removed, but there are still errors all over the place. There are a few different things you could do—you could deploy the GA classic code without calling the page view, but then you're still sending classic hits. You could just define and empty array and ignore the errors, but then you're losing data. What we decided to do was to write a solution that would capture the hits, parcel them up and send them to the data layer for handling with GTM.

We start by defining _gaq, the classic GA array, as either whatever's there or empty, in the same way we would for the data layer.

var _gaq = _gaq || [];

We then define an empty object and array, to be used later if there's ecommerce stuff involved.

var gaq_ecommerce_object = {};
var gaq_products_array = [];

We're creating a function called data_layer_push here, which accepts two arguments—the first being an array, the second being a 'callback' function we'll run on that array.

var data_layer_push = function(arr, callback) {

The push method for the array 'arr' is being redefined here.

  arr.push = function(e) {

This is just ensuring normal array push functioning—do what you would have done before, push 'e' into array 'arr', by using 'call' on the default Array object push.

    Array.prototype.push.call(arr, e);

But then after that, we're getting it to run the callback function on the array 'arr'—so the 'callback' function will be applied to array 'arr' every time something is pushed into 'arr'.

    callback(arr);
  };
};

We're then running that function where _gaq is the array, and the callback function is one we're about to define.

data_layer_push(_gaq, function(newgaq) {

We're setting a variable here to be the latest item pushed into the _gaq array.

      var data_layer_values = newgaq[newgaq.length - 1]

Then we run through the values that have just been pushed to _gaq and sort out what sort of hit they are, and then return to the data layer the relevant information in data layer form.

      switch(data_layer_values[0]){

If it's a pageview hit, we push an object to the data layer with an identifying event name and the path as additional parameters.

        case '_trackPageview':
        dataLayer.push({
          'event': 'hard_coded_ga_pv',
          'hard_coded_ga_path': data_layer_values[1]
        });
        break;

If it's an event, we push we push an object to the data layer with an identifying event name and all the relevant parameter values.

        case '_trackEvent':
        dataLayer.push({
          'event': 'hard_coded_ga_event',
          'hard_coded_ga_event_category': data_layer_values[1],
          'hard_coded_ga_event_action': data_layer_values[2],
          'hard_coded_ga_event_label': data_layer_values[3],
          'hard_coded_ga_event_value': data_layer_values[4]
        });
        break;

Transactions are handled differently in Classic than in Universal—they're split into multiple parts, "_addTrans", which specifies the transaction-level information, which we assign to the empty gaq_ecommerce_object object we created earlier in EE format.

        case '_addTrans':
          window.gaq_ecommerce_object.id = data_layer_values[1];
          window.gaq_ecommerce_object.hard_coded_ga_trans_affiliation = data_layer_values[2];
          window.gaq_ecommerce_object.revenue = data_layer_values[3];
          window.gaq_ecommerce_object.tax = data_layer_values[4];
          window.gaq_ecommerce_object.shipping = data_layer_values[5];
        break;

Then items are added using '_addItem', and we push those to the empty gaq_products_array we created earlier as an object in EE if there's ecommerce stuff involved.

        case '_addItem':
        window.gaq_products_array.push({
          'id':data_layer_values[2],
          'name':data_layer_values[3],
          'category':data_layer_values[4],
		      'price':data_layer_values[5],
          'quantity':data_layer_values[6]
        })
        break;

Finally "_trackTrans" is run and we push the whole object to the data layer in EE format.

        case '_trackTrans':
        dataLayer.push({
          'event': 'hard_coded_ga_trans',
          'ecommerce': {
            'purchase if there's ecommerce stuff involved':{
              'actionField' : window.gaq_ecommerce_object,
              'products' : window.gaq_products_array
                        }
                        }
    })
        break;
      }
    });

Next steps

Once you’ve added this in a custom HTML tag to fire everywhere, you’ll need to configure some GA tags to fire on the data layer pushes. The solution is also available in full on the Measurelab Github. Please leave a comment if you’ve got any questions or ideas for improvement!


Suggested content

Measurelab awarded Google Cloud Marketing Analytics Specialisation

At the start of the year, if you’d asked us whether Measurelab would be standing shoulder to shoulder with Europe’s biggest consultancies by September, we would've been surprised. Not because we don't believe in ourselves, but because these things feel so distant - until suddenly, they’re not. So, here it is: we’ve been awarded the Marketing Analytics Services Partner Specialisation in Google Cloud Partner Advantage. What’s the big deal? In Google’s own words (with the obligatory Zs): “Spec

Will Hayes11 Sept 2025

BigQuery AI.GENERATE tutorial: turn SQL queries into AI-powered insights

BigQuery just got a major upgrade, you can now plug directly into Vertex AI using the new AI.GENERATE function. Translation: your analytics data and generative AI are now best friends, and they’re hanging out right inside SQL. That opens up a whole world of new analysis options for GA4 data, but it also raises some questions: * How do you actually set it up? * What’s it good for (and when should you avoid it)? * Why would you batch the query? Let’s walk through it step by step. Step 1: H

Katie Kaczmarek3 Sept 2025

How to start forecasting in BigQuery with zero training

If you’d told me five years ago that I’d be forecasting product demand using a model trained on 100 billion time points… without writing a single line of ML code… I probably would’ve asked how many coffees you’d had that day ☕️ But its a brand new world. And it’s possible. Let me explain What is TimesFM? TimesFM is a new foundation model from Google, built specifically for time-series forecasting. Think of it like GPT for time, instead of predicting the next word in a sentence, it predicts t

Katie Kaczmarek14 Jul 2025