Skip to content

Instantly share code, notes, and snippets.

@i8ramin
Created August 4, 2017 15:07
Show Gist options
  • Save i8ramin/b2b55fdd2ad3b2f455e6e7f78198b025 to your computer and use it in GitHub Desktop.
Save i8ramin/b2b55fdd2ad3b2f455e6e7f78198b025 to your computer and use it in GitHub Desktop.
<script>
/**
* Percent page viewed plugin
*
* Usage:
* bamPercentPageViewed.init({ option : 'value' });
*
* Options:
* trackDelay : 1500 - The delay (in ms) before a scroll position is considered stable (reduce scroll bouncers)
* percentInterval : 10 - Track every 10% the page is scrolled | Default: 25
* callback : function(data){ console.log(data); } - The callback for the previous page scroll position
*
*/
(function(bamPercentPageViewed) {
/**
* Throttle function borrowed from:
* Underscore.js 1.5.2
* http://underscorejs.org
* (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Underscore may be freely distributed under the MIT license.
*/
function throttle(t,n){var e,u,l,r=null,a=0,i=function(){a=new Date,r=null,l=t.apply(e,u)};return function(){var o=new Date;a||(a=o);var c=n-(o-a);return e=this,u=arguments,c>0?r||(r=setTimeout(i,c)):(clearTimeout(r),r=null,a=o,l=t.apply(e,u)),l}}
// Array.prototype.find Polyfill cause IE sucks
Array.prototype.find||Object.defineProperty(Array.prototype,"find",{value:function(t){if(null==this)throw new TypeError('"this" is null or not defined');var r=Object(this),e=r.length>>>0;if("function"!=typeof t)throw new TypeError("predicate must be a function");for(var n=arguments[1],o=0;e>o;){var i=r[o];if(t.call(n,i,o,r))return i;o++}return void 0}});
// Default options
var defaultOptions = {
trackDelay: 1500,
percentInterval: 25,
callback: null,
};
// Last scroll action
var lastScroll = null;
// FIXME: make this thing a proper class that has access to this.options;
var options = {};
// Keep track of already scrolled depths, so we don't trigger event again
var scrollDepths = [];
/**
* Initialize the tracker and pass in optional overrides
* @param object
* @return bamPercentPageViewed
*/
bamPercentPageViewed.init = function(opts) {
// Options
if (typeof(opts) == typeof({})) {
for (var property in opts) {
if (options.hasOwnProperty(property)) {
defaultOptions[property] = opts[property];
}
}
}
// FIXME
options = opts;
// Get the amount of screen currently in view
processScroll();
// Do not break the chain
return this;
};
/**
* Page scroll percentage
* @return int
*/
function scrollPercent() {
return Math.ceil(((scrollPosition() + viewportHeight()) / pageHeight()) * 100);
}
/**
* Page height
* @return int
*/
function pageHeight() {
return Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
}
/**
* Viewport height
* @return int
*/
function viewportHeight() {
return (window.innerHeight || document.documentElement.clientHeight || document.getElementsByTagName('body')[0].clientHeight || 0);
}
/**
* Current window scroll position
* @return int
*/
function scrollPosition() {
return (window.pageYOffset || document.documentElement.scrollTop) - (document.documentElement.clientTop || 0);
}
/**
* Scroll percentage by interval
* @return int
*/
function scrollPercentByInterval(percentInterval) {
var multiplier = parseInt(scrollPercent() / (parseInt(percentInterval) - 0.0000000001));
return (multiplier * parseInt(percentInterval));
}
/**
* Process scroll
*/
function processScroll() {
var depth;
var trackedDepth;
var path = document.location.pathname;
var url = document.location.href;
if (lastScroll === null || (new Date().getTime()) - lastScroll >= parseInt(defaultOptions.trackDelay)) {
depth = scrollPercentByInterval(defaultOptions.percentInterval);
trackedDepth = scrollDepths.find(function (entry) {
return (entry.path === path) && (entry.depth === depth);
});
if (!trackedDepth && (depth > 0) && typeof(options.callback) === 'function') {
scrollDepths.push({
path: path,
depth: depth
});
options.callback.call(window, {
depth: depth,
url: url,
path: path,
scrollPosition: scrollPosition(),
viewportHeight: viewportHeight(),
pageHeight: pageHeight(),
});
}
}
}
/**
* Listen for throttled window scroll event
*/
window.onscroll = throttle(function() {
lastScroll = new Date().getTime();
setTimeout(processScroll, parseInt(defaultOptions.trackDelay) + 25);
}, 100);
}(window.bamPercentPageViewed = window.bamPercentPageViewed || {}));
</script>
<script>
// init the plugin
bamPercentPageViewed.init({
trackDelay: 1000,
callback: function (data) {
window.analytics.track('Scroll Depth', data);
}
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment