Copyright Johan Kronberg 2009-2024 Latest posts RSS feed X: johankronberg LinkedIn Profile GitHub: krompaco
Site generated by: Record Collector

Google Analytics Enhanced Ecommerce Features with Tag Manager

The Enhanced Ecommerce Features in Google Analytics are pretty cool. So are the features of Google Tag Manager.

Steve Celius has a blog post on Google Analytics Tracking and Customization for Episerver Commerce where he shows some of the features.

But if you are using Google Tag Manager on your site his approach won't work.

Using Google Tag Manager the function ga() is not accessible and you need to push data into the Data Layer instead.

I will demo some concepts that me and my two great NetRelations workmates Linus Boström and Martin Jansson put in place. The concepts will work for any Commerce site and you shouldn't have any problems implementing them in an Episerver Commerce site.

Data Layer Variable Names

These are names we've named and defined as Macros to be able to use in Tag Manager Rules and Containers. Keep in mind that these could have any name and the name is set by you.

  • eventCategory
  • eventAction
  • noInteraction

Other property names like event and ecommerce and ecommerce.* are standard properties controlled and handled by Google.

Product list impressions

Any time we show a product item somewhere we get it as a product JSON string and output it in a data attribute.

<li data-impression-json="<%#: GetStatisticsJson() %>">..</li>

In the rendered markup.

<li data-impression-json="{&quot;list&quot;:&quot;Product list block X&quot; .. ">..</li>

The JSON string here is stripped of line breaks and then HTML encoded. With breaks and decoded looks something like this.

{
    "list": "Product list block X",
    // The ID for the variant
    "id": "37673647",
    "name": "Very cool t-shirt 1",
    // The / is important in category value, it allows you
    // to drill down in category tree performance in GA
    "category": "Clothing/T-shirts",
    "brand": "Cool Shirts Inc.",
    // We put all the selected variant choices here
    "variant": "L Black",
    // And use two dimension slots for each variant choice
    "dimension1": "L",
    "dimension2": "Black",
    "dimension3": "The overlying product ID",
    // Position within the block
    "position": 2,
    "price": "99.00"
}

This allows to retrieve all product lists on onDomReady and just send one impression event to GA. We attach the Ecommerce data to a separate event instead of running the risk of disturbing the regular pageview tracking.

// Find all product items in all lists on page
$('[data-impression-json]').each(function () {
    var raw_json = string.decodeHtmlEntities(
        $(this).attr('data-impression-json')
    );

    try {
        var parsed_json = $.parseJSON(raw_json);
        data.ecommerce.impressions.push(parsed_json);
    }
    catch (e) {}
});

if (data.ecommerce.impressions.length > 0) {
    // Send to Google
    data.event = 'contentLoadEvent';
    data.eventCategory = 'Content';
    data.eventAction = 'Ecommerce list loaded';
    data.noInteraction = 1;
    dataLayer.push(data);
}

// The decodeHtmlEntities function in our plugin for reference
decodeHtmlEntities: function (string) {
    string = string
        .replace('&amp;',  '&')
        .replace('&quot;', '"')
        .replace('&#39;',  '\'')
        .replace('&lt;',   '<')
        .replace('&gt;',   '>');

    return string;
}

Add to cart

An example of where we push an event when the post action page is loaded instead of in a client side onClick-event. This is more stable since we don't need to set a delay on the clicks to be more sure of the Data Layer event has time to be sent to Google.

<script>
    dataLayer.push({
        'event': 'addToCartEvent',
        'eventCategory': 'Products',
        'eventAction': 'Added product to cart',
        'ecommerce': {
            'add': {
                'actionField': {
                    'list': '<%= TheCurrentListNameFromBackend %>'
                },
                'products': [<%= ProductJsonForAdddedItem %>]
            }
        }
    });
</script>

Remove from cart

When an item is removed or amount is decreased we render this on next page load.

<script>
    dataLayer.push({
        'event': 'productEvent',
        'eventCategory': 'Products',
        'eventAction': 'Removed from cart',
        'ecommerce': {
            'remove': {
                // Fetching the basket product row JSON from a hidden field
                // since the product is gone from the basket now
                'products': [<%= ProductJsonFromHiddenFormInput %>]
            }
        }
    });
</script>

Product page loaded

When a product page loads we output a detail event.

<script>
    dataLayer.push({
        'event': 'detailsLoaded',
        'eventCategory': 'Content',
        'eventAction': 'Details loaded',
        'noInteraction': 1,
        'ecommerce': {
            'detail': {
                'products': [<%= ProductJson %>]
            }
        }
    });
</script>

Checkout steps

Put in the HTML of step 2 to record step 1 and the option selected.

<script>
    dataLayer.push({
        'event': 'checkoutEvent',
        'eventCategory': 'Checkout',
        'eventAction': 'Set option',
        'noInteraction': 1,
        'ecommerce': {
            'checkout_option': {
                'actionField': {
                    'step': 1,
                    'option': '<%= SomeDeliveryOptionFromBackend %>'
                }
            }
        }
    });
</script>

Transaction

Record the transaction, remember to check and only output this once per TransactionID. You don't want the tracked totals to get messed up by, for example, the user refreshing the page.

The product JSON for a basket should have a quantity property and no list property.

<script>
    dataLayer.push({
        'event': 'transactionLoaded',
        'eventCategory': 'Transaction',
        'eventAction': 'Transaction loaded',
        'noInteraction': 1,
        'ecommerce': {
            'currencyCode': '<%= CurrencyCode %>',
            'purchase': {
                'actionField': {
                    'id': '<%= TransactionID %>',
                    'revenue': '<%= Revenue %>',
                    'tax': '<%= Tax %>',
                    'shipping': '<%= Shipping %>'
                },
                'products': [<%= ProductJsonForItemsInBasket %>]
            }
        }
    });
</script>

Published and tagged with these categories: Episerver, ASP.NET, Google Analytics