Expand my Community achievements bar.

SOLVED

Mouse scroll tracking in the current page

Avatar

Level 3

Hi ,

As per my requirement, When user trying to scroll mouse down to the page. Need to track how much percent user scroll in the page.

getPercentPageViewed Plug-in-  user views and pass the value into a variable on the next page view.

I want to track in the current page based on % scrolled.

Example :

$(window).on('scroll', function(){

  var s = $(window).scrollTop(),

      d = $(document).height(),

      c = $(window).height();

  var scrollPercent = (s / (d - c)) * 100;

  if(scrollPercent < 25){

scrollPercent =0;

  }

  else if(scrollPercent <50){

scrollPercent =25;

  }

  else if(scrollPercent <75){

scrollPercent =50;

  }

  else ...................

  console.clear();

  console.log(scrollPercent);

})

Can you please suggest me how to configure this script in DTM ?

Thanks - Satish

1 Accepted Solution

Avatar

Correct answer by
Level 9

DTM does not currently have a built-in way for triggering an event based on percent of page view, or even scroll position in general.  But there are a number of ways you can approach this, utilizing various components of DTM.  Below is my take on it.


First, create a Data Element with the following:

Name:  pagescroll_percent

Type: JS Object

Path: _satellite._scrollTracker.percent

Default Value:  leave this blank

Leave other options unchecked, and Remember this value for Pageview, and then Save Changes.

Summary: This will be a data element for you to reference which page scroll percent milestone was triggered.


Next, create a Page Load Rule.

Name it whatever you like, e.g. "Page Scroll Percent Config".

Under Conditions, set Trigger rule at to DOM Ready

Under Javascript / Third Party Tags, click Add New Script.

Tag Name: whatever you like, e.g. "config"

Type: Non-Sequential Javascript

Enable (check) Execute Globally

In the Code Box, add the following code:

_satellite._scrollTracker = {

    callback: function() {

        try {

            var _sT = _satellite._scrollTracker,

                h = document.documentElement,

                b = document.body,

                st = 'scrollTop',

                sh = 'scrollHeight',

                p = 0,

                pv = false;

            this.percent = this.percent || {};

            p = Math.round((h[st] || b[st]) / ((h[sh] || b[sh]) - h.clientHeight) * 100);

            pv = (p >= 25) && !this.percent[25] && 25 || pv;

            pv = (p >= 50) && !this.percent[50] && 50 || pv;

            pv = (p >= 75) && !this.percent[75] && 75 || pv;

            pv = (p >= 100) && !this.percent[100] && 100 || pv;

            if (pv) {

                this.percent[pv] = true;

                _sT.percent = pv;

                _satellite.track('pagescroll_percent_hit');

            }

            if (this.percent[25] && this.percent[50] && this.percent[75] && this.percent[100]) {

                window.clearInterval(_satellite._scrollTracker.interval);

            }

        } catch (e) {}

    }

};

try {

    _satellite._scrollTracker.interval = window.setInterval(_satellite._scrollTracker.callback, 250);

} catch (e) {}

Summary: This is your main "engine".   This is currently configured to trigger at 25%, 50%, 75%, and 100%, based on your original code attempt posted. This is also configured to only trigger once per milestone per page view. When a milestone is reached, the js object reference for the data element is updated, and the direct call rule is invoked.  When all milestones are hit, the code stops listening for milestones.

Additional Notes:

Overall, this code is setup in such a way to always trigger a given milestone if the % is reached or exceeded.  This is usually best practice, because having it trigger every time the % is hit will make for muddy data if a visitor scrolls up and down the page (similar in principle to tracking video % milestones vs. scrubbing the video). However, there are a few caveats about it.

Firstly, because javascript offers no native scroll event to listen for, you have to calculate the current percent and then wrap it in a setInterval to "poll" it regularly.  You should always take page performance into consideration when implement code of this nature. I currently have this set to evaluate every 250 milliseconds.  I have found in practice this is IMO the "sweet spot" between waiting too long (potentially missed tracking) vs. too short (too much of a performance hit). But feel free to adjust this as you see fit.

Secondly, due to the nature of browser functionality vs. the code being evaluated every 250 milliseconds (or whatever you may change it to), milestone events may not trigger sequentially, or you may also see multiple milestone hits triggered all at once.  For example, if you scroll down too fast, especially for shorter pages, and/or if you set the setInterval to eval at a longer interval, you may for example scroll past the 25 and 50% mark.  Because of timing, you may see the 50% milestone trigger first,  but the 25% milestone should trigger at the next setInterval eval.

Another scenario is you may already be scrolled down to e.g. 55% of the page, and then you refresh the page. Most browsers reload the page and put you at the previous scroll position. In scenario, you will see a DC call for both 25% and 50% trigger one right after the other. But overall, you should only see a given milestone % trigger once (if reached) per page view.

Thirdly, as previously mentioned, this code is currently setup to trigger each milestone only once per page view.  This will be a problem for Single Page Apps (SPAs) where new content is loaded without fully refreshing the page. If this is your scenario, you will need to update the code to reset the milestone % flags.  The easiest way to do this is to overwrite the flag object with an empty object:

_satellite._scrollTracker.callback.percent = {};

When or where you actually do this depends on how your SPA is setup, so I leave that exercise to you. Also, if your SPA involves "infinite scrolling pages", then you will need to update the code to factor in offset of last "page" scrolled to and that's where things start to get hard and very site-specific, so you're definitely on your own with that.


Finally, create a Direct Call Rule.

Name it whatever you like, e.g. "Page Scroll Percent Hit".

Under Conditions, in the String text field, enter pagescroll_percent_hit

Summary: This will be the rule that triggers whenever your scroll percent milestones are reached.

Notes: You can reference the pagescroll_percent Data Element to know which milestone % triggered the rule. 

For example, in the Adobe Analytics > eVars section, you can set eVar1="%pagescroll_percent%" to record e.g. eVar1 with a value of "25" (when 25% is triggered). 

Or, you can setup some code to trigger a specific Adobe Analytics event for each milestone. In the Adobe Analytics section, in the Custom Page Code code box, you can do something like this:

var percent = _satellite.getVar('pagescroll_percent');

var event = '';

switch (percent) {

    case 25:

        event = 'event1';

        break;

    case 50:

        event = 'event2';

        break;

    case 75:

        event = 'event3';

        break;

    case 100:

        event = 'event4';

        break;

}

if (event) {

    s.events = event;

    s.linkTrackEvents = event;

    s.linkTrackVars = 'events';

}

.josh

View solution in original post

3 Replies

Avatar

Correct answer by
Level 9

DTM does not currently have a built-in way for triggering an event based on percent of page view, or even scroll position in general.  But there are a number of ways you can approach this, utilizing various components of DTM.  Below is my take on it.


First, create a Data Element with the following:

Name:  pagescroll_percent

Type: JS Object

Path: _satellite._scrollTracker.percent

Default Value:  leave this blank

Leave other options unchecked, and Remember this value for Pageview, and then Save Changes.

Summary: This will be a data element for you to reference which page scroll percent milestone was triggered.


Next, create a Page Load Rule.

Name it whatever you like, e.g. "Page Scroll Percent Config".

Under Conditions, set Trigger rule at to DOM Ready

Under Javascript / Third Party Tags, click Add New Script.

Tag Name: whatever you like, e.g. "config"

Type: Non-Sequential Javascript

Enable (check) Execute Globally

In the Code Box, add the following code:

_satellite._scrollTracker = {

    callback: function() {

        try {

            var _sT = _satellite._scrollTracker,

                h = document.documentElement,

                b = document.body,

                st = 'scrollTop',

                sh = 'scrollHeight',

                p = 0,

                pv = false;

            this.percent = this.percent || {};

            p = Math.round((h[st] || b[st]) / ((h[sh] || b[sh]) - h.clientHeight) * 100);

            pv = (p >= 25) && !this.percent[25] && 25 || pv;

            pv = (p >= 50) && !this.percent[50] && 50 || pv;

            pv = (p >= 75) && !this.percent[75] && 75 || pv;

            pv = (p >= 100) && !this.percent[100] && 100 || pv;

            if (pv) {

                this.percent[pv] = true;

                _sT.percent = pv;

                _satellite.track('pagescroll_percent_hit');

            }

            if (this.percent[25] && this.percent[50] && this.percent[75] && this.percent[100]) {

                window.clearInterval(_satellite._scrollTracker.interval);

            }

        } catch (e) {}

    }

};

try {

    _satellite._scrollTracker.interval = window.setInterval(_satellite._scrollTracker.callback, 250);

} catch (e) {}

Summary: This is your main "engine".   This is currently configured to trigger at 25%, 50%, 75%, and 100%, based on your original code attempt posted. This is also configured to only trigger once per milestone per page view. When a milestone is reached, the js object reference for the data element is updated, and the direct call rule is invoked.  When all milestones are hit, the code stops listening for milestones.

Additional Notes:

Overall, this code is setup in such a way to always trigger a given milestone if the % is reached or exceeded.  This is usually best practice, because having it trigger every time the % is hit will make for muddy data if a visitor scrolls up and down the page (similar in principle to tracking video % milestones vs. scrubbing the video). However, there are a few caveats about it.

Firstly, because javascript offers no native scroll event to listen for, you have to calculate the current percent and then wrap it in a setInterval to "poll" it regularly.  You should always take page performance into consideration when implement code of this nature. I currently have this set to evaluate every 250 milliseconds.  I have found in practice this is IMO the "sweet spot" between waiting too long (potentially missed tracking) vs. too short (too much of a performance hit). But feel free to adjust this as you see fit.

Secondly, due to the nature of browser functionality vs. the code being evaluated every 250 milliseconds (or whatever you may change it to), milestone events may not trigger sequentially, or you may also see multiple milestone hits triggered all at once.  For example, if you scroll down too fast, especially for shorter pages, and/or if you set the setInterval to eval at a longer interval, you may for example scroll past the 25 and 50% mark.  Because of timing, you may see the 50% milestone trigger first,  but the 25% milestone should trigger at the next setInterval eval.

Another scenario is you may already be scrolled down to e.g. 55% of the page, and then you refresh the page. Most browsers reload the page and put you at the previous scroll position. In scenario, you will see a DC call for both 25% and 50% trigger one right after the other. But overall, you should only see a given milestone % trigger once (if reached) per page view.

Thirdly, as previously mentioned, this code is currently setup to trigger each milestone only once per page view.  This will be a problem for Single Page Apps (SPAs) where new content is loaded without fully refreshing the page. If this is your scenario, you will need to update the code to reset the milestone % flags.  The easiest way to do this is to overwrite the flag object with an empty object:

_satellite._scrollTracker.callback.percent = {};

When or where you actually do this depends on how your SPA is setup, so I leave that exercise to you. Also, if your SPA involves "infinite scrolling pages", then you will need to update the code to factor in offset of last "page" scrolled to and that's where things start to get hard and very site-specific, so you're definitely on your own with that.


Finally, create a Direct Call Rule.

Name it whatever you like, e.g. "Page Scroll Percent Hit".

Under Conditions, in the String text field, enter pagescroll_percent_hit

Summary: This will be the rule that triggers whenever your scroll percent milestones are reached.

Notes: You can reference the pagescroll_percent Data Element to know which milestone % triggered the rule. 

For example, in the Adobe Analytics > eVars section, you can set eVar1="%pagescroll_percent%" to record e.g. eVar1 with a value of "25" (when 25% is triggered). 

Or, you can setup some code to trigger a specific Adobe Analytics event for each milestone. In the Adobe Analytics section, in the Custom Page Code code box, you can do something like this:

var percent = _satellite.getVar('pagescroll_percent');

var event = '';

switch (percent) {

    case 25:

        event = 'event1';

        break;

    case 50:

        event = 'event2';

        break;

    case 75:

        event = 'event3';

        break;

    case 100:

        event = 'event4';

        break;

}

if (event) {

    s.events = event;

    s.linkTrackEvents = event;

    s.linkTrackVars = 'events';

}

.josh

Avatar

Level 1
HI Josh, Can I get your help setting this up via Launch? I have tried to let it fire on the page, however when I check in the console the value of the data elemnt it comes empty: _satellite.getVar("pagescroll_percent") ""

Avatar

Level 1

@oscarjavieros were you able to set this via Launch? wouldn't mind seeing if you could share your solution