Scroll Tracking
Scroll tracking can be very helpful to track content engagement for an article or webpage.
Some interesting questions it can help you answer are: What percentage of an article has been read by a user? Has an article been completed by a user? How many users make it 50% of the way through an article? What percentage of users make it to the bottom of the page?
Using the code below, a great query for this data model would be the median of ratio_max
grouped by page title. You could then see which pages have the best scroll-depth engagement.
Here’s an example that shows how to model and send events based on a user’s scrolling on a page.
Due to the nature of browsers, this recipe will only capture scroll depth for 75-85% of article reads. Many times, a session will end (e.g. person closes a tab) before an event can be tracked. The data is great for understanding patterns in scroll depth and comparing content performance, but wouldn’t be viable as a per-user source of truth.
Make sure to include YOUR_KEEN_PROJECT_ID
and YOUR_KEEN_WRITE_KEY
.
<html>
<head>
<meta charset="utf-8">
<script src="http://d26b395fwzu5fz.cloudfront.net/keen-tracking-1.0.5.js"></script>
<style>
body {
/* Demo page content; not required for regular use. */
height: 9000px;
}
</style>
</head>
<body>
<h1>Open your developer console</h1>
<a href="./index.html">Test Page Unload</a>
<script>
/*
Define client
*/
var client = new Keen({
projectId: 'YOUR_KEEN_PROJECT_ID',
writeKey: 'YOUR_KEEN_WRITE_KEY'
});
// Log attempts
client.on('recordEvent', console.log);
Keen.debug = true;
Keen.ready(function(){
// Set listener to sample engagement on page unload
// Define data model for 'page-unload' events here
setUnloadListener(function(){
return {
scroll_info: sampleScrollState()
};
});
});
/*
Define scroll-handler
*/
var winScroll = Keen.utils.listener('window');
var pixel = 0, pixel_max = 0;
winScroll.on('scroll', sampleScrollState);
/* Demo logger; not required for regular use. */
var scrollSampler = setInterval(function(){
console.log(sampleScrollState());
}, 2000);
/*
Scroll-depth sampling
*/
function sampleScrollState() {
pixel = getScrollOffset() + getWindowHeight();
if (pixel > pixel_max) {
pixel_max = pixel;
}
return {
'pixel': pixel,
'pixel_max': pixel_max,
'ratio': parseFloat( Number(pixel / getScrollableArea()).toFixed(2) ),
'ratio_max': parseFloat( Number(pixel_max / getScrollableArea()).toFixed(2) )
}
}
function getWindowHeight() {
return window.innerHeight || document.documentElement.clientHeight;
}
function getScrollOffset() {
return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
}
function getScrollableArea() {
var body = document.body, html = document.documentElement;
return Math.max( body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight ) || null;
}
/*
Interrupt unload to perform synchronous request
*/
function setUnloadListener(dataModelHandler){
console.log('Called: setUnloadListener()')
var attachEvent, whichEvent;
var handler = function(){
handleUnloadEvent(dataModelHandler);
};
if (window.onpagehide || window.onpagehide === null) {
window.addEventListener('pagehide', handler, false);
}
else {
attachEvent = window.attachEvent || window.addEventListener
whichEvent = window.attachEvent ? 'onbeforeunload' : 'beforeunload';
attachEvent(whichEvent, handler);
}
}
function handleUnloadEvent(dataModelHandler){
var request = getXHR();
var url = client.url('events', 'page-unload');
if (request) {
url += '?api_key=' + client.writeKey();
url += '&data=' + encodeURIComponent(
btoa(
JSON.stringify(
dataModelHandler()
)
)
);
request.open('GET', url, false);
request.send(null);
}
}
function getXHR() {
if (window.XMLHttpRequest && ('file:' != window.location.protocol || !window.ActiveXObject)) {
return new XMLHttpRequest;
} else {
try { return new ActiveXObject('Microsoft.XMLHTTP'); } catch(e) {}
try { return new ActiveXObject('Msxml2.XMLHTTP.6.0'); } catch(e) {}
try { return new ActiveXObject('Msxml2.XMLHTTP.3.0'); } catch(e) {}
try { return new ActiveXObject('Msxml2.XMLHTTP'); } catch(e) {}
}
return false;
}
</script>
</body>
</html>
pixel
and pixel_max
are how far down a user has scrolled into the page, including the effective browser window. With pixel_max,
you can see the farthest a user scrolled into the page.
ratio
and ratio_max
are the same but in terms of the % of total scrollable area on a page.
You could also find out how long a user is on a page by subtracting the page-unload
time and page-load time.
var load_time = new Date().getTime();
// later
var time_on_page = new Date().getTime() - load_time;