Implementing Google Analytics Enhanced Ecommerce can be complicated, and especially product and promo (internal banner) tracking can be tricky. The “concept” in this blog post addresses a solution to some common implementation and tracking problems:
- Consistent naming and values across impressions, clicks and add to cart.
- HTTP Request Limit for impression tracking. The limit for a HTTP request to the Google Analytics endpoint is 8Kb (or more exactly 8192 bytes). If the number of objects (products) in the array is larger than your defined number, let’s say 35, and a visitor has selected to show 100 products, this solution will automatically send the data in 3 hits to avoid hitting the 8Kb limit.
- If there are many different modules on a page, it can be problematic to integrate data for all those modules into 1 impression tracking script. Modules in this context can be Bestsellers, Related Products, Other Customers Also Bought, Newly arrived, category page etc.
About this Product & Promo Tracking concept
Most of the problems listed above are solved using javascript. To use this concept you have to use Google Tag Manager, and some of the code is jQuery. Althoug scripts in this article is very custom, it’s based on the Google Tag Manager Enhanced Ecommerce documentation.
The reason for calling this for a “concept” is because although some of the code can more or less just be copied and used as it is, you will still have to do some server side programming and adapt the concept to your needs.
The key to success is pid
In my scripts you will find a variable called pid. This is a key that we use for all click tracking (add to cart, product click, promo click, add to wishlist and add to compare). This key must be unique. If for example Product A is found in 2 different modules on the same page (ex. listed both in Related Products and Other Customers Also Bought), pid must be different in those two modules.
To create a unique pid you can ex. use the database ID for the product + part of the module name. Let’s say the database ID for Product A is 123. Then pid could be Rel123 in the Related Products module, and Oth123 in the Other Customers Also Bought module.
Impression data implementation
This concept is using the JavaScript Array push() Method to overcome the problem with integrating data from different modules into one Enhanced Ecommerce Impression tracking script.
Step 1 – define your product and promo array
1 2 3 4 |
<script> var product=[]; var promo=[]; </script> |
Products and promo (internal banners) will be in 2 different arrays called product and promo. Add the script above before your global Google Tag Manager script, ex. before </head>.
Product Impression script
The script example below should be implemented into the different product modules. The code below is an example code for the “Bestsellers” module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<script> product.push( {// Product 1 'pid':'Bes1773', // Unique ID across modules 'name':'HTC Touch HD', // Product Name 'id':'6000', // Product SKU/ID 'brand':'HTC', // Product Brand 'category':'Phones', // Product Category 'price': '122.00', // Product Price 'list':'Bestsellers', // Product List 'position':'1', // Product Position 'url':'/phones/htc-touch-hd' // Product URL }, // Product 2 {'pid':'Bes1720','name':'iPhone', 'id':'6009', 'brand':'Apple', 'category':'Phones', 'price': '103.00', 'list':'Bestsellers', 'position':'2', 'url':'/phones/iphone'} ); </script> |
As mentioned, it’s vital that pid is unique since all product click tracking will use data from the product impression script.
Promo/banner Impression code
The script example below should be implemented into the different internal banner modules.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<script> promo.push( {// Promo 1 'pid':'prs91', // Unique ID across modules 'id': 'JUNE_PROMO13', // Promo ID 'name': 'June Sale', // Promo Name 'creative': 'banner1', // Promo Creative 'position': 'slot1', // Promo Position 'url': '/sale' // Promo URL }, // Promo 2 {'pid':'prs92', 'id': 'FREE_SHIP13', 'name': 'Free Shipping Promo', 'creative': 'skyscraper1', 'position': 'slot2', 'url': '/free-shipping'} ); </script> |
Promo/internal banner must also have a unique pid.
Enhanced Ecommerce Impression tracking scripts
Generic Impression tracking script
The generic impression tracking script should be added to all pages that has product data except the product detail page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<script> if (product.length > 0 || promo.length > 0) { var maxProducts = 35; // Max objects that will be sent with 1 hit. var ecomm = product.concat(promo); // Merge product & promo into 1 array that we use in the add to cart & click tracking. while(product.length || promo.length) { var p1 = product.splice(0,maxProducts); // Split the product array into arrays with max 35 objects var p2 = promo.splice(0,maxProducts); // Split the promo array into arrays with max 35 objects dataLayer.push({ 'ecommerce': { 'promoView': { 'promotions': p2 }, 'impressions': p1 }, 'event': 'impression', // GTM Event for Impression tracking 'eventCategory':'Ecommerce','eventAction':'Impression' }); }; }; </script> |
This script should be implemented at the end of your page (before </body>). I have implemented it like this since I also send other page type specific data with this script.
You can implement this script in a Custom HTML tag in Google Tag Manager if you like. If you do that, the Trigger should either be Dom Ready or Window Loaded, or you can create a more advanced Trigger that checks if the product or promo array contains data. I haven’t tested this.
Impression tracking script for Product Detail Page
This script should be added to the product detail page.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
<script> var ecomm = product.concat(promo); // Merge product & promo into 1 array that we use in the add to cart & click tracking. dataLayer.push({ 'ecommerce': { 'impressions': product, 'promoView': { 'promotions': promo }, 'detail': { 'products': [{ 'name': 'iPhone', 'id': '6009', 'price': '103.00', 'brand': 'Apple', 'category': 'Phones' }] } }, 'event': 'impression', // GTM Event for Impression tracking 'eventCategory': 'Ecommerce', 'eventAction': 'Impression' }); </script> |
This script should be implemented at the end of your page (before </body>). I have implemented it like this since I also send other page type specific data with this script.
The script doesn’t check for number of objects in the product or promo array since this is normally not necessary on a product page.
Tagging of Buttons and Links
All product and promo links, and all buttons (add to cart, wishlist and compare) have to be tagged in some way. I’m using data attributes in this example. This concept requires 2 different data attributes:
- data-pid: This attribute has the unique ID for the product or banner.
- data-type: This attribute tells the script what kind of click this is:
- link
- cart
- promo
- wishlist
- compare
The HTML example below demonstrates this tagging.
1 2 3 4 5 |
<a href="/phones/iphone-6" data-pid="Bes1773" data-type="link">iPhone 6</a> <button type="button" data-pid="Bes1773" data-type="cart">Add to Cart</button> <button type="button" data-pid="Bes1773" data-type="wishlist">Add to Wishlist</button> <button type="button" data-pid="Bes1773" data-type="compare">Add to Compare</button> <a href="/sale" data-pid="prs91" data-type="promo"><img src="june-banner.png" alt="June Sale" /></a> |
Code for tracking Add to Cart, Product & Promo Clicks, Add to Wishlist and Add to Compare
This script takes the pid data attribute value, and matches it against the ecomm array. If a match is found, data about the product or internal banner will be sent to Google Analytics as either Add to Cart, Product Click, Promo Click, Add to Wishlist or Add to Compare.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
<script> function item_by_pid(pid){ for( var i = 0, j = ecomm.length; i < j; i++ ){ if( ecomm[i]['pid'] == pid ){ return ecomm[i]; } } return false; } $(function(){ $('[data-pid][data-type]').on('click', function( event ){ var $this = $( this ), id = $this.attr( 'data-pid' ), type = $this.attr( 'data-type' ), pid = item_by_pid( id ); if( !pid ){ return; } switch (type) { case 'cart': // Track Add to Cart dataLayer.push({'event':'addToCart', 'ecommerce':{ 'add':{'actionField':{'list':pid.list}, 'products': [{ 'name':pid.name, 'id':pid.id, 'price':pid.price, 'brand':pid.brand, 'category':pid.category, 'quantity':'1', 'position':pid.position }]}}}); break; case 'link': // Track Link clicks dataLayer.push({'event':'productClick', 'ecommerce':{ 'click':{'actionField':{'list':pid.list}, 'products':[{ 'name':pid.name, 'id':pid.id, 'price':pid.price, 'brand':pid.brand, 'category':pid.category, 'position':pid.position }]}}, 'eventCallback':function() {document.location = pid.url}}); break; case 'promo': // Track Promo/Banner clicks dataLayer.push({'event':'promoClick', 'ecommerce':{ 'promoClick':{'promotions':[{ 'id':pid.id, 'name':pid.name, 'creative':pid.creative, 'position':pid.position }]}}, 'eventCallback':function(){document.location = pid.url;}}); break; case 'wishlist': // Track Add to Wishlist clicks dataLayer.push({'event':'addToWishlist', 'list':pid.list, 'name':pid.name, 'quantity':'1'}); break; case 'compare': // Track Add to Compare clicks dataLayer.push({'event':'addToCompare', 'list':pid.list, 'name':pid.name, 'quantity':'1'}); break; } }); }); </script> |
This script should be implemented on all pages that has product data. You can add this script to a Custom HTML Tag in Google Tag Manager if you like.
Widening product data with Dimensions or Metrics
If you want to widen your product data with either dimensions or metrics, there is one caveat you have to be aware of.
Let’s say you want to track product discount. You want to track the discount in percentage as a dimension, but you also want to track the amount as a metric. The dimension should be sent both with impression and add to cart, but the metric should only be sent with add to cart. Your Discount Percentage dimension is in your Google Analytics setup dimension1, and your Discount Amount metric is metric1.
If you want to send the dimension or metric data with the impression, use the dimension or metric variable names from Google Analytics, ex. dimension1 and metric1. If you don’t want to send the data with the impression, call the variable something else.
In the code example below, the discount in percentage (25) will be sent with the impression since it’s named dimension1. The discount amount (24.40) will not be sent since it’s named discount.
1 2 3 4 5 6 7 |
{ 'pid':'Bes1773', // Unique ID across modules ....code........... 'url':'/phones/htc-touch-hd', // Product URL 'dimension1': '25', 'discount': '24.40' } |
To send the discount in both percentage and amount with Add to Cart, you will have to change the Add to Cart code into this:
1 2 3 4 5 6 7 8 9 10 11 |
case 'cart': // Track Add to Cart clicks dataLayer.push({'event':'addToCart', 'ecommerce':{ 'add':{'actionField':{'list':pid.list}, 'products': [{ 'name':pid.name, ....code................ 'dimension1':pid.dimension1, // Discount in percent 'metric1':pid.discount // Discount amount }]}}}); ..........more code...................... |
Google Tag Manager setup
Google Tag Manager Variables
Add the Variables relevant for your e-Commerce store to Google Tag Manager.
Variable Name | Data Layer Variable Name | Comment |
---|---|---|
productName | name | For Wishlist & Compare tracking |
productList | list | For Wishlist & Compare tracking |
productListClick | ecommerce.click.actionField.list | For tracking product clicks as GA Events also |
productNameClick | ecommerce.click.products.0.name | For tracking product clicks as GA Events also |
productQuantity | quantity | For Wishlist & Compare tracking |
promoClickName | ecommerce.promoClick.promotions.0.name | For tracking promo clicks as GA Events also |
promoCreative | ecommerce.promoClick.promotions.0.creative | For tracking promo clicks as GA Events also |
addToCartList | ecommerce.add.actionField.list | Track Add to Cart as GA Events as well |
addToCartName | ecommerce.add.products.0.name | Track Add to Cart as GA Events as well |
addToCartQuantity | ecommerce.add.products.0.quantity | Track Add to Cart as GA Events as well |
Google Tag Manager Event Triggers
Add the Event Triggers that is relevant for your e-Commerce store to Google Tag Manager.
Trigger Name | Event Name | Comment |
---|---|---|
addToCart | addToCart | Add to Cart trigger |
addToCompare | addToCompare | Add to Compare trigger |
addToWishlist | addToWishlist | Add to Wishlist trigger |
impression | impression | Impression tracking trigger |
productClick | productClick | Product Click trigger |
promoClick | promoClick | Promo/banner Click trigger |
Google Tag Manager Tags setup
Enhanced Ecommerce Impression tracking
What | Setting | Comment |
---|---|---|
Tag Type | Universal Analytics | |
Track Type | Event | |
Enable Enhanced Ecommerce Features | True | |
Use Data Layer | True | |
Trigger | impression | |
Event Category | {{eventCategory}} | |
Event Action | {{eventAction}} | |
Event Label | Leave empty | |
Event Value | Leave empty | |
Non-Interaction Hit | True | GA Event that doesn't affect Bounce Rate |
Add to Cart tracking
What | Setting | Comment |
---|---|---|
Tag Type | Universal Analytics | |
Track Type | Event | |
Enable Enhanced Ecommerce Features | True | |
Use Data Layer | True | |
Trigger | addToCart | |
Event Category | Add to Cart | |
Event Action | {{addToCartList}} | Track Enhanced List Name as Action |
Event Label | {{addToCartName}} | Track Product Name as Label |
Event Value | {{addToCartQuantity}} | Track Number of products added to cart as Value |
Non-Interaction Hit | False |
Product Click tracking
What | Setting | Comment |
---|---|---|
Tag Type | Universal Analytics | |
Track Type | Event | |
Enable Enhanced Ecommerce Features | True | |
Use Data Layer | True | |
Trigger | productClick | |
Event Category | Product Click | |
Event Action | {{productListClick}} | Track Enhanced List Name as Action |
Event Label | {{productNameClick}} | Track Product Name as Label |
Event Value | Leave empty | |
Non-Interaction Hit | False |
Promo (Internal banner) Click tracking
What | Setting | Comment |
---|---|---|
Tag Type | Universal Analytics | |
Track Type | Event | |
Enable Enhanced Ecommerce Features | True | |
Use Data Layer | True | |
Trigger | promoClick | |
Event Category | Internal Banner | |
Event Action | {{promoClickName}} | Track Promo Name as Action |
Event Label | {{promoCreative}} | Track Creative as Label. If you don't send this data, leave empty. |
Event Value | Leave empty | |
Non-Interaction Hit | False |
Add to Wishlist tracking
What | Setting | Comment |
---|---|---|
Tag Type | Universal Analytics | |
Track Type | Event | |
Enable Enhanced Ecommerce Features | False | |
Trigger | addToWishlist | |
Event Category | Add to Wishlist | |
Event Action | {{productList}} | Track Enhanced List Name as Action |
Event Label | {{productName}} | Track Product Name as Label |
Event Value | {{productQuantity}} | Track Number of products added to Wishlist as Value |
Non-Interaction Hit | False |
Add to Compare tracking
What | Setting | Comment |
---|---|---|
Tag Type | Universal Analytics | |
Track Type | Event | |
Enable Enhanced Ecommerce Features | False | |
Trigger | addToCompare | |
Event Category | Add to Compare | |
Event Action | {{productList}} | Track Enhanced List Name as Action |
Event Label | {{productName}} | Track Product Name as Label |
Event Value | {{productQuantity}} | Track Number of products added to Compare as Value |
Non-Interaction Hit | False |
Some final words
All tracking described in this blog post is done using Google Analytics Events. Especially impression tracking will increase the number of hits to your Google Analytics account. Although I haven’t experienced any problems with this method, you should be aware of the Google Analytics Collection Limits and Quotas.
If you have solved the problems described in the beginning of this blog post in a different way, or if you think that the concept or scripts could be improved (I’m not a programmer), feel free to add a comment.
Hello, thanks for great article !
I have a problem that bother me several days.
There is a pic ( ),
This pic’s data-pid and data-type are added after mouseover event.
And the code in this article ( $(‘[data-pid][data-type]’).on(‘click’, function( event ){ )
can’t detect when click pic.
Is that the reason data-pid and data-type aren’t set before dom ready?
And what can I do ..
Thanks
Hi Enix
If I understand your problem correctly, on the image there is a mouseover event that hinders the click tracking, is that correct?
I can’t promise anything, but could you drop your code as a comment, and I could try to take a look.
“Is that the reason data-pid and data-type aren’t set before dom ready?”
I’m not sure I understand this question.
Eivind
Hi Eivind,
Great post! I have two quick questions:
1. Under Google Tag Manager Variables shouldn’t you also include “eventCategory” and “eventAction”?
2. Under Promo (Internal banner) Click tracking shouldn’t the Event be “promoClick” instead of “productClick”?
Hi Andrew
Thanks for your feedback.
1. No. If you look at the Tag Setup part you will see that I use ex. {{addToCartList}} as Event Action.
2. You are correct. Great spotted!
There is one more thing I should have added to this blog post. I doesn’t use “list” for product detail page (and I don’t think you should), which means that you will get “undefined” Event Action for products added to cart from the product page. That is of course not correct. I will try to update the blog post soon to the latest version of my setup.
Regards,
Eivind
Hi Eivind,
Great article!
I implemented it this way and GTM is complaining that I must have variables for {{eventCategory}} and {{eventAction}}
Am I missing something here?
Hi Andrei
If I understand your problem correctly I think you have forgotten to create those Data Layer Variables in your GTM setup. You have to create 1 Data Layer Variable called eventCategory and 1 Data Layer Variable called eventAction.
Regards,
Eivind
Hey Eivind,
I’ve followed this post with great interest and spent the weekend implementing, debugging and generally just playing with the various settings in GTM – thank you for putting this together! As a relative newbie to GTM (coming from the likes of Adobe Analytics, Tealium etc) I found this post incredibly useful.
One question I did have for you – specifically regarding the Add To Cart functionality.
On my test website, I have a single button that adds a bundle of products to the cart with a single click (it adds a product and an accessory). I want to keep them split this way and not pass a Bundle PID or Bundled product into GTM/GA.
How would you recommend I change the code so this happens?
Thanks again!
R
I would implement the data-pid with multiple ids splitted by some char, and before entering the case function, check whether there are multiple pids. If so, push by a for loop depending on the size of the split.
Hope that give you some sense.
That is a great article! I wonder of it can be simplified to adjuat into angularjs, Without jquery.
Trying your Impression tracking scripts but didn’t work at all.
Is there any update maybe on 2017?
I have this method running on ecommere stores without problem. However, sometimes I have used a different approach, and that is to split the ecommerce object in GTM instead.
Hi Eivind,
Nice post,very clear steps of GTM implementation.
Just wanted to know why did you opted the product Impressions as Events instead of Pageview, even Enhanced Ecommerce (UA) Developer Guide suggests to use product Impressions as pageviews.
Hi Ullas
There are at least 4 reasons for this:
1. As mentioned in the blog post, the limit for a HTTP request to the Google Analytics endpoint is 8Kb. If the HTTP request is above 8Kb, data will not be sent to GA at all (including your pageviews). In cases like this you will have to split the object, and then you can’t use a pageview.
2. Using GTM Event triggers gives you more control, and you will not have a setup where sometimes a regular pageview sends the Enhanced Data, and sometimes not. This will lead you into trouble most likely.
3. Very often data isn’t available on pageview. You could of course use a DOM Ready trigger instead, but this doesn’t help you with point 1.
4. Often you send the data to other places than Google Analytics. Ex. like demonstrated with this method:
https://www.savio.no/google-tag-manager/generic-string-enhanced-ecommerce-product-values-gtm
If you base everything on GTM Event Triggers for sending Ecommerce Data, you will be more prepared for other use cases as well.
Regards,
Eivind
Hi Savio, thanks for sharing.
I think the question in the first comment is that possible to refer a gtm variable in jquery.
I’m trying myself to do something like $({{customvlookup}}).on(‘click’, function( event )
But it doesn t work. Is that possible please ?
The main idea is to use jquery with a vlookup to handle all my click event.
Something like :
$(‘vlookupdivelement’).click(function() {
dataLayer.push({
‘event’: ‘Fire Event GA’,
‘event.category’: ‘{{vlookupcat}}’,
And so on
I doesn t work. Maybe i miss something. Could you help me please ? I’m new in gtm.
Thanks a lot,
Alan
Hi Eivind,
I am sending Product list views (impressions). I have many of them on a single page like 200 at once. I am sending them in chunks like this
while (impressionProducts.length > 0) {
var impressionChunks = impressionProducts.splice(0, 35);
dataLayer.push({
'ecommerce': {
'currencyCode': currencyCode, // Local currency is optional.
'impressions': impressionChunks
},
'event': 'impressionPush', // GTM Event for Impression tracking
'eventCategory': 'Ecommerce', 'eventAction': 'Product Impression'
});
}
But this is not sending all of them. It only sends last chunk of impressions. Could you please suggest what I am doing wrong
Hello Eivind, this is a great post.
I have a question. At the purchase stage, how do you track product position and list? Should we use cookies or an other method? Because at Analytics list view I see the products but purchases has no list or position data.
Regards,
Vassilis.
Hi
For purchase stage, you don’t track product position and list.
Google Analytics will automatically attribute list clicks to sales.
Regards,
Eivind