Skip to Content

scrollDepth()

Initialize scroll depth tracking for basic page engagement data.

JavaScript February 7, 2021

Usage

This function runs automatically, so it is not called manually. Is this incorrect?

Additional Notes

This function listens for (and times) the initial scroll by the user as well as when (if) the user reaches the end of the page. To supplement this data, implement MaxScroll using the autotrack.js library.

Source File

Located in /assets/js/modules/analytics.js on line 1147.

1 Hook

Find these filters and actions in the source code below to hook into them. Use wp.hooks.doAction() and wp.hooks.addFilter() in your JavaScript file.

Filters
"nebulaPreFooterSelector"
Need a new filter hook? Request one here.

Actions
This function has no action hooks available. Request one?

JavaScript
nebula.scrollDepth = async function(){
    if ( window.performance ){ //Safari 11+
        let scrollReady = performance.now();
        let reachedBottom = false; //Flag for optimization after detection is finished
        let excessiveScrolling = false; //Flag for optimization after detection is finished
        let lastScrollCheckpoint = nebula.dom.window.scrollTop(); //Set a checkpoint of the current scroll distance to subtract against later
        let totalScrollDistance = 0; //Down and up distance
        let excessiveScrollThreshold = nebula.dom.document.height()*2; //Set the threshold for an excessive scroll distance

        nebula.maxScrollDepth = 0; //This needs to be accessed from multiple other functions later
        nebula.updateMaxScrollDepth(); //Update it first right away on load (the rest will be throttled)

        let scrollDepthHandler = function(){
            //Only check for initial scroll once
            nebula.once(function(){
                nebula.scrollBegin = performance.now()-scrollReady; //Calculate when the first scroll happens
                if ( nebula.scrollBegin > 250 ){ //Try to avoid autoscrolls on pageload
                    let thisEvent = {
                        category: 'Scroll Depth',
                        action: 'Began Scrolling',
                        scrollStart: nebula.dom.window.scrollTop() + 'px',
                        timeBeforeScrollStart: Math.round(nebula.scrollBegin)
                    };
                    thisEvent.label = 'Initial scroll started at ' + thisEvent.scrollStart;
                    nebula.dom.document.trigger('nebula_event', thisEvent);
                    ga('send', 'event', thisEvent.category, thisEvent.action, thisEvent.scrollStart, thisEvent.scrollStartTime, {'nonInteraction': true}); //Event value is time until scrolling.
                }
            }, 'begin scrolling');

            //Check scroll distance periodically
            nebula.throttle(function(){
                //Total Scroll Distance
                if ( !excessiveScrolling ){
                    totalScrollDistance += Math.abs(nebula.dom.window.scrollTop() - lastScrollCheckpoint); //Increase the total scroll distance (always positive regardless of scroll direction)
                    lastScrollCheckpoint = nebula.dom.window.scrollTop(); //Update the checkpoint
                    if ( totalScrollDistance >= excessiveScrollThreshold ){
                        excessiveScrolling = true; //Set to true to disable excessive scroll tracking after it is detected

                        nebula.once(function(){
                            let thisEvent = {
                                category: 'Scroll Depth',
                                action: 'Excessive Scrolling',
                                label: 'User scrolled ' + excessiveScrollThreshold + 'px (or more) on this page.',
                            };
                            nebula.dom.document.trigger('nebula_event', thisEvent);
                            ga('send', 'event', thisEvent.category, thisEvent.action, thisEvent.label, {'nonInteraction': true});
                        }, 'excessive scrolling');
                    }
                }

                nebula.updateMaxScrollDepth();

                //When user reaches the bottom of the page
                if ( !reachedBottom ){
                    if ( (nebula.dom.window.height()+nebula.dom.window.scrollTop()) >= nebula.dom.document.height() ){ //If user has reached the bottom of the page
                        reachedBottom = true;

                        nebula.once(function(){
                            let thisEvent = {
                                category: 'Scroll Depth',
                                action: 'Entire Page',
                                distance: nebula.dom.document.height(),
                                scrollEnd: performance.now()-(nebula.scrollBegin+scrollReady),
                            };

                            thisEvent.timetoScrollEnd = Math.round(thisEvent.scrollEnd);

                            nebula.dom.document.trigger('nebula_event', thisEvent);
                            ga('send', 'event', thisEvent.category, thisEvent.action, thisEvent.distance, thisEvent.timetoScrollEnd, {'nonInteraction': true}); //Event value is time to reach end
                            window.removeEventListener('scroll', scrollDepthHandler);
                        }, 'end scrolling');
                    }
                }

                //Stop listening to scroll after no longer necessary
                if ( reachedBottom && excessiveScrolling ){
                    window.removeEventListener('scroll', scrollDepthHandler); //Stop watching scrolling– no longer needed if all detections are true
                }
            }, 1000, 'scroll depth');
        };

        window.addEventListener('scroll', scrollDepthHandler); //Watch for scrolling ("scroll" is passive by default)

        //Track when the user reaches the end of the content
        if ( jQuery('#footer-section').length ){
            let footerObserver = new IntersectionObserver(function(entries){
                entries.forEach(function(entry){
                    if ( entry.intersectionRatio > 0 ){
                        let thisEvent = {
                            category: 'Scroll Depth',
                            action: 'Reached Footer',
                            label: 'The footer of the page scrolled into the viewport'
                        };

                        nebula.dom.document.trigger('nebula_event', thisEvent);
                        ga('send', 'event', thisEvent.category, thisEvent.action, thisEvent.label, {'nonInteraction': true});

                        nebula.updateMaxScrollDepth();
                        footerObserver.unobserve(entry.target); //Stop observing the element
                    }
                });
            }, {
                rootMargin: '0px', //0px uses the actual viewport bounds, 100% is double the viewport
                threshold: 0.1 //How much of the element needs to be in view before this is triggered (this is a percentage between 0 and 1)
            });

            //Observe the pre-footer section (or whatever element is after the main content area)
            let preFooterSelector = wp.hooks.applyFilters('nebulaPreFooterSelector', '#footer-section'); //This should be the first section after the "content"
            footerObserver.observe(jQuery(preFooterSelector)[0]); //Observe the element
        }

        //if ( nebula.analytics.metrics.maxScroll ){ //Trying this for all visitors, but may limit to this custom metric if too many events...
            window.addEventListener('beforeunload', function(e){ //Watch for the unload to send max scroll depth to GA (to avoid tons of events)
                nebula.updateMaxScrollDepth(); //Check one last time

                let thisEvent = {
                    category: 'Scroll Depth',
                    action: 'Max Scroll Depth',
                    maxScrollPixels: nebula.maxScrollDepth,
                    maxScrollPercent: Math.round(100*(nebula.maxScrollDepth/(nebula.dom.document.height()-window.innerHeight))) //Round to the nearest percent
                };

                thisEvent.description = 'The user reached a maximum scroll depth of ' + thisEvent.maxScrollPercent + '% (' + thisEvent.maxScrollPixels + 'px) when the page was unloaded.';

                nebula.dom.document.trigger('nebula_event', thisEvent);
                ga('set', nebula.analytics.metrics.maxScroll, thisEvent.maxScrollPercent); //Set the custom metric to the max percent
                ga('send', 'event', thisEvent.category, thisEvent.action, thisEvent.description, {'nonInteraction': true}); //Ideally this would send only once per page per session– and only if that page hadn't reached 100% previously in the session... Consider localstorage
            });
        //}
    }
};

Override

To override or disable this JavaScript function, simply redeclare it with the exact same function name.

JavaScript
nebula.scrollDepth = function(){
    //Write your own code here, leave it blank, or return false.
}