One of our specialities here at InfoTrust is helping ecommerce businesses leverage their web analytics to make better data-driven marketing decisions. This typically starts with installing Google’s Universal Analytics web analytics software and utilizing all of the functionality that is offered with Enhanced Ecommerce tracking capabilities.
Enhanced Ecommerce provides you with a complete picture of what customers on your site are seeing, interacting with and purchasing.
One of the ways you track what your customers are seeing is with product impressions (whenever a user sees an image or description of your products on your website).
Normally, you track what products users see or impressions by simply adding an array of product objects to the DataLayer. These represent the products seen on the page, meaning when any page loads with product images/descriptions, data is sent to Google Analytics that a user saw those specific products. This works well.
However, there is a major issue with this method. Sometimes you are sending impressions for products that the user never actually sees. This can happen when your page scrolls vertically and some products are off the page or below the fold.
Here are the results for the search term “Linensâ€. Currently, you can see sixteen products listed in the search results. However, in the normal method of sending product impressions, a product impression would be sent for every product on the page.
So, in reality this is what we are telling Google Analytics that the user is seeing (every single product on the page).
Obviously, no one’s screen looks like this, but by sending all products as an impression, we are effectively saying that our customer saw all 63 products. What happens if the user never scrolls past the 16 products shown in the first screenshot?
We are greatly skewing the impressions for the products on the bottom of the page, because often times, users are not scrolling the entire length of the page (and therefore not seeing the additional products).
This could cause you to make incorrect assumptions about how well a product is selling based off of position.
The solution: Scroll-based impression tracking!
Here is how it works at a high level:
- Instead of automatically adding all product impressions to the DataLayer, we add it to another variable just for temporary storage. Meaning, we do not send all the products loaded on a page directly to Google Analytics, but rather just identify the products that loaded on the page.
- When the page loads, we actually see what products are visible on the page (ones “above the fold†or where the user can actually see them) and add only those products to the DataLayer for product impressions. Now we don’t send any other product impressions unless they are actually visible to the user.
- Once the user starts to scroll, we start capturing all the products that haven’t been seen before. We continue to capture these products until the user stops scrolling for a certain amount of time.
- We then batch all of those products together and send them to the DataLayer as product impressions.
- If the user starts to scroll again, we start checking again. However, we never send the same product twice on the same page. If they scroll to the bottom then back up, we don’t send the first products twice.
Using our example on the “Linen†search results, right away we would send product impressions for the first 16 products. Then, let’s say the user scrolled halfway down the page and stopped. We would then send product impressions for products 18 through 40. The user then scrolls to the bottom of the page so we would send product impressions for 41 through 63. Finally the user scrolls back to the top of the page before clicking on the first product. No more impressions would be sent as impressions for all products have already been sent.
The result: Product impressions are only sent as users actually navigate through the pages and can see the products. This is a much more accurate form of product impression tracking since it reflects actual user navigation.
Note: For those developers reading this and wanting to get to the how-to, keep reading. Marketers: Send the rest to your developers to get more accurate tracking or get in touch with us and we can help!
The Technical How To Guide
First off, at this point our code is reliant on the jQuery library (although we would like to remove this dependency in the future).
So, on the server side, write your code to store all your product objects in an array, something like this:
<script>
var products_storage = [];
<?php
foreach($products as $product) {
echo “products_storage.push({
‘id’: ‘{$product->id}’,
‘sku’: ‘{$product->sku}’,
‘position’: ‘{$product->position}’,
‘creative’: ’tile’,
‘list’: ‘Search Page’});” . “n”;
}
?>
</script>
Next, we need to make sure each product element on the page has some way of tying it to the product ID we stored in the array above and a shared class that applies to all products.
For example, here we use a data attribute on the div for the product and a class called product_box:
<div class=’product_box’ data-product-id=’1′>
<img src=’images/product.jpg’>
<a href=’/product/linen’ class=’product_name’>linen 1</a>
<div class=’product_price’>$12.99</div>
</div>
Next is the code to actually send the product impressions based off of scrolling:
<script>
var debug = false;
$(document).ready(function () {
var sendItems = [];
var sentItems = [];
var scrollTimeout;
var initial = true;
$(document).scroll(function () {
if (initial === false) {
clearTimeout(scrollTimeout);
detectVisible();
scrollTimeout = setTimeout(doneScroll, 700);
if (debug) console.log(‘scroll’);
} else {
initial = false;
if (debug) console.log(‘done load’);
detectVisible();
doneScroll();
}
});
function getProduct(id) {
for (var x = 0; x < products_storage.length; x++) {
if (products_storage[x].id == id) {
return products_storage[x];
}
}
}
function doneScroll() {
if (debug) console.log(‘done scroll’);
if (debug) console.log(‘Ids to be sent: ‘ + sendItems.join(‘,’));
if (debug) console.log(‘Ids already sent: ‘ + sentItems.join(‘,’));
var counter = 0;
while (sendItems.length > 0) {
counter++;
//get the product id we need to send and take it out of the items needed to be sent
var productId = sendItems.pop();
var product = getProduct(productId);
ga(‘ec:addImpression’, product);
//add the product id to the list of ids already sent so we don’t send duplicates
sentItems.push(tempItem);
}
if (counter > 0) {
//only send event to ga if we had at least one impression added
ga(‘send’,’event’,’Scroll Tracking’,’scroll’,window.location.href);
}
clearTimeout(scrollTimeout);
}
function detectVisible() {
var winHeight = $(window).height();
var winOffset = $(document).scrollTop();
var minY = winOffset;
var maxY = winOffset + winHeight;
$(‘.product_box’).each(function () {
var visible = false;
var itemTop = $(this).offset().top;
var itemBottom = itemTop + $(this).height();
var divId = $(this).data(‘product-id’);
if (debug) console.log(‘min: ‘ + minY + ‘ max: ‘ + maxY + ‘ top: ‘ + itemTop);
if ((itemTop >= minY && itemTop < maxY) || (itemBottom >= minY && itemBottom < maxY)) {
if (sentItems.indexOf(divId) === -1 && sendItems.indexOf(divId) === -1) {
sendItems.push(divId);
}
}
});
if (debug) console.log(sendItems);
}
detectVisible();
});
</script>
We have started to test this code on a few sites and so far it seems to be working well. The best part is most of this coding can be accomplished using a tag management system (we’ve already prototyped this in Google Tag Manager, Tealium, Signal and others).
One note of caution: This will increase your number of hits to GA as they are individual events for each group of products that become “viewableâ€. So, if you are nearing a limit on your number of hits per month, you may want to consider upgrading to Google Analytics Premium.
If you have any questions or comments, let us know!