Skip to Content
Menu

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.

Was this page helpful? Yes No


    A feedback message is required to submit this form.


    Please check that you have entered a valid email address.

    Enter your email address if you would like a response.

    Thank you for your feedback!

    Source File

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

    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 ){ //Limiting this event to when this custom metric is used because of the number of events this records
                window.addEventListener('beforeunload', function(e){ //Watch for the unload to send max scroll depth to GA (to avoid tons of events). Note: this event listener invalidates BFCache in Firefox...
                    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.
    }