For the full custom tracking code, including product list tracking, see: http://help.littledata.io/setup-guides/how-to-setup-littledatas-shopify-app-with-a-custom-tracking-script

<script name="littledata-tracking-tag">
        var LittledataLayer = {
   webPropertyID: '',
            cartPollInterval: 2000, //milliseconds between checking for new addsToCart
};
(function(){
//LittledataLayer to provide product details for Fix Google Analytics app
//Version CUSTOM


LittledataLayer.ecommerce = {
'currencyCode' : '{{shop.currency}}',
'impressions' : []
};

{%if product%}
var product = {{ product | json }};
if (product && !product.error) {
LittledataLayer.ecommerce.detail = {products: []}
LittledataLayer.ecommerce.detail.products.push({
id: product.id,
name: product.title,
price: (product.price/100).toFixed(0),
brand: product.vendor,
category: product.type,
variants: product.variants.map(function(variant) { return { id: variant.id, sku : variant.sku } }),
quantity: 1
});
}
{% elsif search %}
var searchResults = {{search.results | json}} || {};
{% else %}
var collectionNames = [];
{% if collection.handle == "all" %}
var collections = {{collections | json}};
{% else %}
var collections = [{{collection | json}}];
{% endif %}

if (collections && !collections.error) {
collections.forEach(function(c){
if (c) collectionNames.push(c.handle);
});
}

var collectionProducts = {{ collection.products | json }};
if (collectionProducts && !collectionProducts.error) {
collectionNames.forEach(function(c){
var pos = 1;
collectionProducts.forEach(function(p){
LittledataLayer.ecommerce.impressions.push({
id: p.id,
name: p.title,
price: (p.price/100).toFixed(0),
brand: p.vendor,
category: p.type,
position : pos++,
variants : p.variants.map(function(variant) { return { id: variant.id , sku : variant.sku } }),
list : location.pathname,
handle : p.handle
})
})
});
}
{% endif%}

LittledataLayer.ecommerce.impressions = getUniqueProducts(LittledataLayer.ecommerce.impressions)

function getUniqueProducts(array){
  var u = {}, a = [];
  for(var i = 0, l = array.length; i < l; ++i){
 if(u.hasOwnProperty(array[i].id)) {
continue;
 }
 a.push(array[i]);
 u[array[i].id] = 1;
  }
  return a;
}

var loadScript = function(url, callback){
var script = document.createElement("script")
script.type = "text/javascript";

if (script.readyState){  //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else {  //Others
script.onload = function(){
callback();
};
}

script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
var ldTracker = function(jQuery){
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');

ga('create', LittledataLayer.webPropertyID, 'auto', {name : 'littledata', allowLinker : true});
ga('littledata.require', 'displayfeatures');
ga('littledata.require','ec');
ga('littledata.require','linker');
ga('littledata.set', 'anonymizeIp', true);
ga('linker:autoLink', ['shopify.com','rechargeapps.com'])

jQuery(document).ready(function(){
var referralExclusion = /(paypal|visa|MasterCard|clicksafe|arcot\.com|geschuetzteinkaufen|checkout\.shopify\.com|checkout\.rechargeapps\.com|portal\.afterpay\.com|payfort)/
if ( referralExclusion.test(document.referrer) ) ga('littledata.set','referrer',null)

if(LittledataLayer && LittledataLayer.ecommerce){
var locationArr = location.pathname.split('/');
//run list and product scripts everywhere
listViewScript();
productPageScript();

if(locationArr[1] == 'cart')
cartPageScript();
}

function removePii(string) {
var piiRegex = {
"email": /[\s&amp;\/,=]([a-zA-Z0-9_.+-]+(\@|%40)[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+)($|[\s&amp;\/,])/,
"postcode": /[\s&amp;\/,=]([A-Z]{1,2}[0-9][0-9A-Z]?(\s|%20)[0-9][A-Z]{2})($|[\s&amp;\/,])/,
};

var dlRemoved = string;
for (key in piiRegex) {
dlRemoved = dlRemoved.replace(piiRegex[key], 'REMOVED');
}
return dlRemoved;
}

ga('littledata.set', 'title', removePii(document.title));
ga('littledata.set', 'location', removePii(document.location.href));
ga('littledata.send', 'pageview')
})

function listViewScript(){
if (!LittledataLayer.ecommerce.impressions || !LittledataLayer.ecommerce.impressions.length) return;
window.setTimeout(function(){
LittledataLayer.ecommerce.impressions.forEach(function(product,index){
if(index <= 30)
ga('littledata.ec:addImpression', product)
})
ga('littledata.send', 'event', 'Ecommerce', 'Impressions', location.pathname, 0,{
nonInteraction: true,
hitCallback: function() {
}
});
},500) // wait for pageview to fire first
}

function productPageScript(){
var product = LittledataLayer.ecommerce.detail
&& LittledataLayer.ecommerce.detail.products
&& LittledataLayer.ecommerce.detail.products[0]

if (product) {
ga('littledata.ec:addProduct', product)

if(typeof localStorage !== 'undefined')
ga('littledata.ec:setAction', 'detail', {list : localStorage && localStorage.list});
else
ga('littledata.ec:setAction', 'detail');

ga('littledata.send', 'event', 'Ecommerce', 'Detail View', String(product.id), product.price, {
nonInteraction: true
});
}

var tracker = new CartTracker();
tracker.onChange(function(changes) {
changes.added.forEach( function(addedProduct) {
if (product) {
product.variant = addedProduct.variant_id;
}

var productPrice = (addedProduct.price/100).toFixed(0);

ga('littledata.ec:addProduct', product || {
id: addedProduct.product_id,
name: addedProduct.product_title,
price: productPrice,
brand: addedProduct.vendor,
category: addedProduct.product_type,
position : 1,
list : location.pathname,
handle : addedProduct.handle,
variant: addedProduct.variant_id
});
ga('littledata.ec:setAction', 'add');
ga('littledata.send', 'event', 'Ecommerce', 'Add to cart', addedProduct.product_id, productPrice);

window.setTimeout(function(){
cartPageScript();
},500)//wait for real cartToken to generate
})

changes.removed.forEach( function (removedProduct) {
if (product) {
product.variant = removedProduct.variant_id;
}


var productPrice = (removedProduct.price/100).toFixed(0);
ga('littledata.ec:addProduct', product || {
id: removedProduct.product_id,
name: removedProduct.product_title,
price: productPrice,
brand: removedProduct.vendor,
category: removedProduct.product_type,
position : 1,
list : location.pathname,
handle : removedProduct.handle,
variant: removedProduct.variant_id
});
ga('littledata.ec:setAction', 'remove');
ga('littledata.send', 'event', 'Ecommerce', 'Removed from cart', removedProduct.product_id, productPrice);
})
})
}

function cartPageScript(){
ga(function() {
  var clientID = ga.getByName('littledata').get('clientId');
jQuery.getJSON('/cart.json',function(cart){
jQuery.post('https://transactions.littledata.io/clientID', {
clientID : clientID,
cartID : cart.token
})
})
});
}

function CartTracker() {
var _cart = typeof localStorage !== 'undefined' && localStorage.ldCart && JSON.parse(localStorage.ldCart)
var _interval;
var _onChangeCbs = [];
var self = this;

this.start = function() {
interval = setInterval(function() {
fetchCart(function(newCart) {
if (JSON.stringify(_cart.items) !== JSON.stringify(newCart.items)) {
console.log('Changes to cart detected');
_onChangeCbs.forEach(function(_onChangeCb) {
_onChangeCb(getChanges(_cart, newCart))
})
_cart = newCart;
localStorage.ldCart = JSON.stringify(newCart);
}

})
}, LittledataLayer.cartPollInterval)
}

function fetchCart(_cb) {
jQuery.getJSON('/cart.json', function(__cart) {
_cb(__cart);
})
}

function getChanges(pastCart, currentCart) {
var pastCartProds = pastCart.items.map(cloneObject)
var currentCardProds = currentCart.items.map(cloneObject)

var changes = pastCartProds.length ? getArrChanges(pastCartProds, currentCardProds) : getArrChanges(currentCardProds, pastCartProds);
function getArrChanges(cart1, cart2) {
var returnVal = cart1
.map(function(pastProd) {
var currentCartProd = cart2.filter(function(prod) {
if (prod.id === pastProd.id) {
prod.analysed = true;
return true
}

return false;
})[0]
var returnProd = cloneObject(pastProd);
if (!currentCartProd) {
returnProd.quantity = -returnProd.quantity;
return returnProd;
}

returnProd.quantity = currentCartProd.quantity - pastProd.quantity;

return returnProd;
})
.concat(cart2.filter(function(currentProd) { // in case cart2 has new prods that were not in cart1
return !currentProd.analysed
}))

return returnVal
}

function unwindProds(arr) {
var returnArr = [];

arr.forEach(function(prod) {
var i;
var clonedProduct;
var quantity = Math.abs(prod.quantity);
clonedProduct = cloneObject(prod);
clonedProduct.quantity = 1;

for (i = 0; i < quantity; i++) {
returnArr.push(clonedProduct);
}
})

return returnArr;
}

function cloneObject (item) {
return JSON.parse(JSON.stringify(item))
}

return {
added: unwindProds(changes
.filter(function (prod) {
return pastCartProds.length ? prod.quantity > 0 : prod.quantity < 0;
})),
removed: unwindProds(changes
.filter(function (prod) {
return pastCartProds.length ? prod.quantity < 0 : prod.quantity > 0;
})),
}
}

this.onChange = function (newCb) { _onChangeCbs.push(newCb) }
this.stop = function () { clearInterval(interval) }

if (!_cart) {
fetchCart(function(newCart) {
_cart = newCart;
localStorage.ldCart = JSON.stringify(newCart);
self.start();
})
} else {
self.start();
}
}
}

if(typeof jQuery === 'undefined'){
loadScript('//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js', function(){
jQuery191 = jQuery.noConflict(true);
ldTracker(jQuery191);
 });
} else {
 ldTracker(jQuery);
}
})()
</script>
Did this answer your question?