Adding Keytiles tracking to a "one-pager" page
In this article we would like to give you some inspiration about how to add Keytiles tracking to "one-pager" pages - so visitor's scroll activity and the visited sections can appear in Keytiles nicely.
Content of this page
- # Prerequisites
- # Step 0 - our HTML code
- # Step 1 - making decisions
- # Step 2 - let's add scripts!
- # Step 3 - let's make it final!
Prerequisites
Before you continue we assume you are already aware of the concept of Hit attributes and you took a quick look on Keytiles <meta> tags too.
Step 0 - our HTML code
Let's assume our HTML looks like this
<html>
<head>
<title>My one-pager page</title>
...
<!-- Keytiles meta tags are present - defining attribs of the one-pager page -->
<meta name="kt:tileId" content="12345" />
<meta name="kt:tileTitle" content="My one-pager page" />
<meta name="kt:tileType" content="article" />
<meta name="kt:tileGroupPath" content="/a/b" />
<meta name="kt:tileUrl" content="https://mywebsite.com/a/b/my-one-pager-page" />
</head>
<body>
<div id="page-content">
<!-- first element of the one-pager -->
<div class="sub-element" id="element1">
<h1>My first element</h1>
... your element content comes here ...
</div>
<!-- second element of the one-pager -->
<div class="sub-element" id="element2">
<h1>My second element</h1>
... your element content comes here ...
</div>
... more elements are here
</div>
<script type="text/javascript">
/**
* Let's assume we have a mechanism in place already which is able
* to signal us if an element arrives to the screen or leaves the screen!
*
* This is our callback function which is invoked by this
* @param {string} subElementId - which element?
* @param {boolean} isOnScreen - TRUE if element is got to the screen - FALSE if went out
*/
function onElementScreenStatusChanged(subElementId, isOnScreen) {
}
</script>
</body>
</html>
Step 1 - making decisions
We need to stop for a second and decide: how would you like to see the visits on the TileView? More specifically: where should those sub-contents belong to in your content structure? Given the fact your "one-pager" page might also belong to somewhere in the structure himself... Right?
Let's continue with this more complicated case - let's respect the structural position of the "one-pager" page fully and let's organize all sub-elements under this!
What does it mean practically? Given the above HTML code and the Keytiles <meta> tags in the <head> section we see clearly:
our "one pager" page has the following hit attributes:
{
"tileId": "12345",
"tileType": "article",
"tileGroupPath": "/a/b",
"tileTitle": "My one-pager page",
"tileUrl": "https://mywebsite.com/a/b/my-one-pager-page"
}
And what we want is that all sub-elements are under this page. The very important hit attribute at this point is: tileGroupPath.
So for every sub-elements we could have the following hit attributes:
{
// we go with a "postfix" style tileId
"tileId": "12345-<sub element id>",
"tileType": "article",
// they all will appear "under" the one-pager page - using its title derivative
"tileGroupPath": "/a/b/<dash-cased title of the one-pager page>",
"tileTitle": "<sub element title>",
// in the url the right way is to use anchors
"tileUrl": "https://mywebsite.com/a/b/my-one-pager-page#<sub element id>"
}
Step 2 - let's add scripts!
Let's add some JavaScript magic to the above HTML! Just go through and read the inline comments!
We are almost ready however we will have one more problem... Can you spot it? :-)
<html>
<head>
<title>My one-pager page</title>
...
<!-- Keytiles meta tags are present - defining attribs of the one-pager page -->
<meta name="kt:tileId" content="12345" />
<meta name="kt:tileTitle" content="My one-pager page" />
<meta name="kt:tileType" content="article" />
<meta name="kt:tileGroupPath" content="/a/b" />
<meta name="kt:tileUrl" content="https://mywebsite.com/a/b/my-one-pager-page" />
</head>
<body>
<script type="text/javascript">
// let's create a global reference to our tracker object
var keytilesTrackerApi = null;
/**
* Our callback method
* @param {KeytilesTrackingApi} keytilesTrackerApiObject- the Keytiles API object instance
*/
function onKeytilesTrackerAvailable(keytilesTrackerApiObject) {
// make tracker available globally
keytilesTrackerApi = keytilesTrackerApiObject;
}
/**
* We need the help of the keytilesTrackingConfig object Keytiles recognizes
*/
var keytilesTrackingConfig = {
// let's keep the auto-tracking mechanism - that
// works well for the one-pager page itself
autoTrackingEnabled: true,
// and setup our callback function which will be called as soon as tracking script was initialized
onTrackerAvailableCallbackFunc: onKeytilesTrackerAvailable
};
/**
* and now let's pull in the tracking script!
*/
(function () {
var kt = document.createElement('script');
kt.type = 'text/javascript';
kt.async = true;
kt.src = document.location.protocol+'//scripts.keytiles.com/tracking/<your-container-id>/stat.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(kt, s);
})();
</script>
<div id="page-content">
<!-- first element of the one-pager -->
<div class="sub-element" id="element1">
<h1>My first element</h1>
... your element content comes here ...
</div>
<!-- second element of the one-pager -->
<div class="sub-element" id="element2">
<h1>My second element</h1>
... your element content comes here ...
</div>
... more elements are here
</div>
<script type="text/javascript">
// we just want to send in hits once / sub-element so we will mark them
var hitSentAlready = {};
/**
* Let's assume we have a mechanism in place already which is able
* to signal us if an element arrives to the screen or leaves the screen!
*
* This is our callback function which is invoked by this
* @param {string} subElementId - which element?
* @param {boolean} isOnScreen - TRUE if element is got to the screen - FALSE if went out
*/
function onElementScreenStatusChanged(subElementId, isOnScreen) {
// if gets to the screen and we have not sent hit yet, do it!
if(isOnScreen && !hitSentAlready[subElementId]) {
// we dont want to do it again
hitSentAlready[subElementId] = true;
// this returns the hit attribs for the one-pager page itself
var onepagerHitAttribs = KeytilesTrackingApi.buildHitOptions();
// let's put together the attribs of the sub-element!
var hitAttribs = {
tileId: onepagerHitAttribs.tileId + '-' + subElementId,
tileType: 'article',
// lets append the one-pager dash-cased title
tileGroupPath: onepagerHitAttribs.tileGroupPath + '/' + onepagerHitAttribs.tileTitle.toLowerCase().replace(/\s/g, "-"),
// lets grab the title from h1 tag
tileTitle: document.getElementById(subElementId).firstChild().innerHtml,
tileUrl: onepagerHitAttribs.tileUrl + '#' + subElementId
};
// fire in the hit!
keytilesTrackerApi.sendPageview(hitAttribs);
}
}
</script>
</body>
</html>
Step 3 - let's make it final!
Our problem is: async script loading...
The onElementScreenStatusChanged() method might be fired easily BEFORE the Keytiles tracking objects become available... If that happens then the KeytilesTrackingApi.buildHitOptions() call will fail because KeytilesTrackingApi is not available yet.
So we need to build a "hit queue" somehow which
a) collects hits until Keytiles tracking becomes available
b) fires the queued hits once this happens
So let's rework the code a bit! To clean up things and separate concerns we create the keytilesTracking helper object to deal with everything we need
The below code was not tested! Use as inspiration only! :-)
<html>
<head>
<title>My one-pager page</title>
...
<!-- Keytiles meta tags are present - defining attribs of the one-pager page -->
<meta name="kt:tileId" content="12345" />
<meta name="kt:tileTitle" content="My one-pager page" />
<meta name="kt:tileType" content="article" />
<meta name="kt:tileGroupPath" content="/a/b" />
<meta name="kt:tileUrl" content="https://mywebsite.com/a/b/my-one-pager-page" />
</head>
<body>
<script type="text/javascript">
/**
* Let's encapsulate every problem into a helper object!
*/
var keytilesTracking = {
_keytilesTrackerApi: null,
_hitQueue: [],
_hitSentAlready: {},
/**
* Our callback method - moved here
* @param {KeytilesTrackingApi} keytilesTrackerApiObject- the Keytiles API object instance
*/
onKeytilesTrackerAvailable: function(keytilesTrackerApiObject) {
keytilesTracking._keytilesTrackerApi = keytilesTrackerApiObject;
// fire the queue!
keytilesTracking._hitQueue.forEach(function(item){
this._assembleAndSendHit(item);
}, keytilesTracking);
keytilesTracking._hitQueue = [];
},
/**
* Initiate a hit sending for a sub-element
*/
sendElementHit: function(subElementId) {
if(keytilesTracking._hitSentAlready[subElementId]) {
// ignore - we already sent this
return;
}
// mark it so we do not send again
keytilesTracking._hitSentAlready[subElementId] = true;
if(keytilesTracking._keytilesTrackerApi) {
// we can send immediately - API is available already
keytilesTracking._assembleAndSendHit(subElementId);
} else {
// push to the queue - we send as soon as API becomes available
keytilesTracking._hitQueue.push(subElementId);
}
},
/**
* private method - creates and sends the hit of a sub-element
*/
_assembleAndSendHit: function(subElementId) {
// this returns the hit attribs for the one-pager page itself
var onepagerHitAttribs = KeytilesTrackingApi.buildHitOptions();
// let's put together the attribs of the sub-element!
var hitAttribs = {
tileId: onepagerHitAttribs.tileId + '-' + subElementId,
tileType: 'article',
// lets append the one-pager dash-cased title
tileGroupPath: onepagerHitAttribs.tileGroupPath + '/' + onepagerHitAttribs.tileTitle.toLowerCase().replace(/\s/g, "-"),
// lets grab the title from h1 tag
tileTitle: document.getElementById(subElementId).firstChild().innerHtml,
// just add the anchor
tileUrl: onepagerHitAttribs.tileUrl + '#' + subElementId
};
// fire in the hit!
keytilesTracking._keytilesTrackerApi.sendPageview(hitAttribs);
}
};
/**
* We need the help of the keytilesTrackingConfig object Keytiles recognizes
*/
var keytilesTrackingConfig = {
// let's keep the auto-tracking mechanism - that
// works well for the one-pager page itself
autoTrackingEnabled: true,
// and setup our callback function which will be called as soon as tracking script was initialized
onTrackerAvailableCallbackFunc: keytilesTracking.onKeytilesTrackerAvailable
};
/**
* and now let's pull in the tracking script!
*/
(function () {
var kt = document.createElement('script');
kt.type = 'text/javascript';
kt.async = true;
kt.src = document.location.protocol+'//scripts.keytiles.com/tracking/<your-container-id>/stat.js';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(kt, s);
})();
</script>
<div id="page-content">
<!-- first element of the one-pager -->
<div class="sub-element" id="element1">
<h1>My first element</h1>
... your element content comes here ...
</div>
<!-- second element of the one-pager -->
<div class="sub-element" id="element2">
<h1>My second element</h1>
... your element content comes here ...
</div>
... more elements are here
</div>
<script type="text/javascript">
/**
* Let's assume we have a mechanism in place already which is able
* to signal us if an element arrives to the screen or leaves the screen!
*
* This is our callback function which is invoked by this
* @param {string} subElementId - which element?
* @param {boolean} isOnScreen - TRUE if element is got to the screen - FALSE if went out
*/
function onElementScreenStatusChanged(subElementId, isOnScreen) {
// if gets to the screen fire the hit
// note: now the filtering is added to the keytilesTracking object!
if(isOnScreen) {
keytilesTracking.sendElementHit(subElementId);
}
}
</script>
</body>
</html>