Light-weight scroll tracking

I covered scroll tracking my previous post: Track and measure scrolling.

This is a light weight version of the script. It should avert any jittery scrolling issues. It has one disadvantage – it only calculates the scroll distances (ie how many pixels is 25% of the page height etc) once on page load, so if the window is resized, the events will fire at the wrong heights. In practice I think this is a small issue in most cases

Implementing the script

The script below can be used in two ways: with or without Google Tag Manager (GTM).

Implementing without GTM

simply add the script to each page, ideally in the section after the universal analytics tag. Remember to change the value of the useGTM variable to false.

Implementing with GTM

This is the recommended approach, as it gives you all the benefits of Google Tag Manager and is far easier to customise and control the way the events come through.
For the purposes of these instructions, I’m assuming that you already have universal analytics working in a GTM container, and have some knowledge of GTM. If you need help with this Google provides a good guide to why and how to use Google Tag Manager

  1. Add the script as a custom HTML tag, set to fire on all pages.
    Scrolling-tag
  2. Add Data Layer Variables for eventCategory, eventAction, eventLabel and eventValue
    Scrolling-variable
  3. Add a trigger, for a custom event with event name=scroll
  4. Add a universal analytics event tag, to fire on the trigger from (3) and get its Category, Action, Label and Value from the variables in (2)
    Scrolling-trigger
  5. Put the container into preview mode and test

The script

This is the script for the custom HTML tag to be added in step 1 above. It should fire on page load for all pages. In the GTM version it fires events into the GTM dataLayer. The non-GTM version fires events directly using the ga() universal analytics function.

Three configuration variables are brought to the top for ease of tweaking:

  • footerElementID=”footer” – if you have a footer in a div or similar, set this to its ID. If you do not, set this to anything, but make sure it does not match any actual element
  • nonInteraction=”true” – by default the events are non-interactive. This impacts whether the event is seen as an interaction for the purposes of calculating bounce rate (see this Google reference). If you are using GTM you can set whether the events are interactive in the GTM setup itself (step (4)), or you can pick up this as a data layer variable. If you are reporting directly to GA, then this variable will be in control.
  • useGTM=true – if true, the code will send events to the GTM dataLayer. If false, the code will use ga(‘send’, {…

Minified version

<script>// <![CDATA[
!(function(){
    var footerElementID="footer",nonInteraction=true,useGTM=true;
    var maxScroll=0,body=document.body,html=document.documentElement,percentScrolled,footerElement=document.getElementById(footerElementID),reportScrollEvent,bodyH=Math.max(body.scrollHeight,body.offsetHeight,html.clientHeight,html.scrollHeight,html.offsetHeight),viewH=Math.max(html.clientHeight,window.innerHeight||0),scrollableH=(footerElement===null)?(scrollableH=bodyH-viewH):(scrollableH=bodyH-viewH-footerElement.offsetHeight);if(useGTM){reportScrollEvent=function(label,value){dataLayer=window.dataLayer||[];dataLayer.push({'event':'scroll','eventCategory':'scroll','eventAction':'scroll','eventLabel':label,'eventValue':value,'nonInteraction':nonInteraction});}}
else{reportScrollEvent=function(label,value){ga('send',{'hitType':'event','eventCategory':'scroll','eventAction':'scroll','eventLabel':label,'eventValue':value,'nonInteraction':nonInteraction});}}
if(window.addEventListener){window.addEventListener("scroll",getScrollHandler());}else if(window.attachEvent){window.attachEvent("onscroll",getScrollHandler());}
function getScrollHandler(){return function(){window.setTimeout(function(){scrolledH=Math.floor(Math.abs(body.getBoundingClientRect().top)),percentScrolled=Math.min(100,Math.floor((scrolledH / scrollableH)*4)*25);if(percentScrolled>maxScroll){maxScroll=percentScrolled;reportScrollEvent(percentScrolled+'%',scrolledH);}},25);}}}());
// ]]></script>

Full version

<script>// <![CDATA[

!(function(){
    
    //settings to check/change
    var footerElementID = "footer", //name of your footer element
        nonInteraction = true, 
        useGTM = true; //true will send events to GTM data layer - change to false if using GA without GTM

    var maxScroll = 0,
        body = document.body,
        html = document.documentElement,
        percentScrolled,
        footerElement = document.getElementById(footerElementID),
        reportScrollEvent,
        bodyH = Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight), //the height of the page body
        viewH = Math.max(html.clientHeight, window.innerHeight || 0), //the height of the view
        //height that can be scrolled. If footer not found, use the height of body, otherwise use body-footer
        scrollableH = (footerElement === null)?(scrollableH = bodyH - viewH):(scrollableH = bodyH - viewH - footerElement.offsetHeight);

    if (useGTM) {
        reportScrollEvent = function (label, value){
            dataLayer = window.dataLayer || [];
            dataLayer.push({
                'event' : 'scroll',
                'eventCategory' : 'scroll',
                'eventAction' : 'scroll',
                'eventLabel' : label,
                'eventValue' : value,
                'nonInteraction' : nonInteraction
            });
        }    
    } 
    else  {
        reportScrollEvent = function (label, value){
            ga('send', {
                'hitType' : 'event',
                'eventCategory' : 'scroll',
                'eventAction' : 'scroll',
                'eventLabel' : label,
                'eventValue' : value,
                'nonInteraction' : nonInteraction
            });
        }   
    }
    
    
    if (window.addEventListener) {
        window.addEventListener("scroll", getScrollHandler());
    } else if (window.attachEvent) {
        window.attachEvent("onscroll", getScrollHandler());
    }

    function getScrollHandler() {
        return function(){
            /* 
               scrollable height is NOT recalculated each time a 
               scroll event fires - changes in window size may result in events being sent incorrectly
            */
            window.setTimeout(function(){
                scrolledH = Math.floor(Math.abs(body.getBoundingClientRect().top)), //the amount scrolled
                //proportion of possible scroll completed, in 25% increments  
                //note 100% maximum, otherwise can report 125% etc, where we have scrolled into the footer 
                percentScrolled = Math.min(100,Math.floor((scrolledH / scrollableH) * 4) * 25); 
                if (percentScrolled > maxScroll) { //event fires once each time a 25% scroll increment in passed
                    maxScroll = percentScrolled;
                    reportScrollEvent(percentScrolled + '%', scrolledH);
                }
            },25);
        }
    }
}());
// ]]></script>

Leave a Reply

Your email address will not be published. Required fields are marked *