Back to Code List
javascript

Scroll Tracking

This plugin is a way track when a user has scrolled to a specific section(s) on the page. It can be very useful for single page sites that need to simulate different areas as page views for analytics.

This plugin can be used to track any type of analytics, or even just custom javascript (for example, a popup if a certain section is viewed).

For more info, view the tooltips file as well as comments in the code.

   tooltips.txt

This plugin provides a way to track different parts of a page when they are scrolled to.

For example, let's say you have a single page site where you have X header links that jump to different sections on the page. The requirement for this scenario says:

    1) If a user clicks on the link in the header, track that specific action as a page view. Then, scroll to the section on the page.
    2) If a user scrolls without clicking, track each section as a page view as they scroll past it.
    3) Only track each section page view once regardless of if it was clicked or scrolled.
    
This can be a little tricky to determine, so a couple parts are required.

First, we need the scroll tracking to fire on certain parts of the page. This plugin is built to handle this by giving a list of ids for your sections to track. You also will provide the code to run when that id is viewed on the page.

Second, for the clicking, it requires that you have some jQuery to track each link click. How to set this up will depend on your situation. There is an example provided in the code.

NOTES:
    -- Please read the inline comments in the code on how to configure this plugin.
    -- This code should run in a global scope any time the page loads.    

   scrollTracking.js

/*START config section*/

    //add click handlers with the two fields as follows to
    //track those sections without instantiating other scroll
    //sections.
    //  _ppv.suspendFlag = 1;
    //  _ppv.flagReset = "<id of section>";
    jQuery('.js-mobile-nav ul li a').click(function(){
        var id = $(this).attr('href');
        var section = id.substr(1,id.length-1);
        _ppv.suspendFlag = 1;
        _ppv.flagReset = section;
    });

    //insert the ids of the elements that will trigger an event
    //followed by a function to run your tracking code when that
    //element is hit on the scroll
    var _ppv_ids = {
        'timeline': function(){ console.log('timeline'); },
        'checklist':function(){ console.log('checklist'); },
        'costs':    function(){ console.log('costs'); },
        'subsidy':  function(){ console.log('subsidy'); },
        'tax':      function(){ console.log('tax'); },
        'plans':    function(){ console.log('plans'); }
    };
    
    //if there is a fixed header (stays at the top of the users page
    //always visible as they scroll), this offset variable must get the
    //height of it. Otherwise, just set as 0.
    var offset = 0;
    if(document.body.clientWidth > 800)
        offset = document.getElementsByClassName('header')[0].scrollHeight;
        
    //change this variable to change the accuracy of tracking. Some
    //browsers scroll differently than others and the placement of
    //elements are different for mobile and desktop. This number
    //should be adjusted to be the highest possible while still able
    //to track all sections properly. This should normally be between 1-10.
    //default is 3 (pixels)
    var accuracy = 3;

/*END config section*/

/*DO NOT edit anything below*/
(function(ids,os,ac){
    window._ppv = {
        suspendFlag: 0,
        flagReset: "",
        offset: os,
        idConfig: ids,
        percConfig: {},
        percMap: {},
        tracked: {},
        accuracy: 3,
        docHeight: Math.max(
            Math.max(document.body.scrollHeight, document.documentElement.scrollHeight), 
            Math.max(document.body.offsetHeight, document.documentElement.offsetHeight), 
            Math.max(document.body.clientHeight, document.documentElement.clientHeight)
        ),
        findScroll: function() {
            var yScroll;
            if (self.pageYOffset) {
                yScroll = self.pageYOffset;
            } else if (document.documentElement && document.documentElement.scrollTop) {
                yScroll = document.documentElement.scrollTop;
            } else if (document.body) {
                yScroll = document.body.scrollTop;
            }
            return yScroll+_ppv.offset;
        },
        calculatePercent: function(cur) {
            return Math.min(Math.round(cur / _ppv.docHeight * 100), 100);
        },
        findPos: function(obj) {
            var curtop = 0;
            if (obj.offsetParent) {	
                do {
                    curtop += obj.offsetTop;	
                } while (obj = obj.offsetParent);
            }
            var perc = _ppv.calculatePercent(curtop);
            return perc;
        },
        checkConfig: function() {
            _ppv.currScroll = _ppv.findScroll();
            _ppv.currPerc = _ppv.calculatePercent(_ppv.currScroll);
            var nearest = Math.floor(_ppv.currPerc/_ppv.accuracy)*_ppv.accuracy;
            if(_ppv.suspendFlag && _ppv.flagReset) {
                var flagPerc = _ppv.percMap[_ppv.flagReset];
                if(nearest == flagPerc){
                    if(!_ppv.tracked[nearest]) {
                        _ppv.percConfig[nearest]();
                        _ppv.tracked[nearest] = 1;
                    }
                    _ppv.flagReset = "";
                    _ppv.suspendFlag = 0;
                }
            } else {
                if(_ppv.percConfig[nearest] && !_ppv.tracked[nearest]){
                    _ppv.percConfig[nearest]();
                    _ppv.tracked[nearest] = 1;
                }
            }
            //console.log(nearest);
        },
        addHandlers: function() {
            if (window.addEventListener) {
                window.addEventListener('load', _ppv.checkConfig, false);
                window.addEventListener('scroll', _ppv.checkConfig, false);
                window.addEventListener('resize', _ppv.checkConfig, false);
            } else if (window.attachEvent) {
                window.attachEvent('onload', _ppv.checkConfig);
                window.attachEvent('onscroll', _ppv.checkConfig);
                window.attachEvent('onresize', _ppv.checkConfig);
            }
        },
        init: function() {
            for(key in _ppv.idConfig){
                var el = document.getElementById(key);
                var perc = _ppv.findPos(el);
                var nearest = Math.floor(perc/_ppv.accuracy)*_ppv.accuracy;
                //console.log("start:", "actual:",perc,"nearest:",nearest)
                _ppv.percConfig[nearest] = _ppv.idConfig[key];
                _ppv.percMap[key] = nearest;
            }
            _ppv.currScroll = _ppv.findScroll();
            _ppv.accuracy = ac > 0 ? ac : _ppv.accuracy;
        }
    };
    _ppv.init();
    _ppv.addHandlers();
})(_ppv_ids,offset,accuracy);