Ajax Link Tracker

. Leave a comment.

There is now a new downloadable version. Ajax Link Tracker 2.2

Take a look at the next generation, MapSurface a modular JSON/On-Demand JavaScript interface that inculdes link tracking.

One of the more interesting aspects of Ajax is the ability to track a user’s interaction within the browser. I wanted to investigate navigation
patterns, so I have written an Ajax based link tracker. If you press the “Ctrl” and "X" keys you will be presented with an overlay which displays links usage by percentage. This functionality was created with JavaScript and a very simple API.

I used the JavaScript page-hijacking technique. On loading, the JavaScript finds all the links and attaches a mousedown event to each link. When a link is clicked the information is stored into a database using Ajax. The link usage overlay is produced dynamically from an Ajax call. I have used a cut down version of the Prototype JavaScript Framework for Ajax calls. The Prototype library is an excellent toolset, but the full version is a little too heavyweight for this site.

Links to the files
www.glennjones.net/javascript/prototype_ajax.js
www.glennjones.net/javascript/linktracker.js

Attaching the Events
I have used Scott Andrew’s cross-browser addEvent function to attach all events. A  window onload event calls the addLinkTracker function when the page loads. This function adds the mousedown events to all the links on the page. If a link does not already have an id it is given one.

[sourcecode language="javascript" wraplines="true"]
function addEvent(elm, evType, fn, useCapture)
{
if (elm.addEventListener) {
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent) {
var r = elm.attachEvent(‘on’ + evType, fn);
return r;
} else {<
elm['on' + evType] = fn;
}
}

addEvent(window, ‘load’, addLinkTracker, false);

function addLinkTracker()
{
if (!document.getElementsByTagName) return false;

linksElements = document.getElementsByTagName(‘a’)
for (var i = 0; i < linksElements.length; i++)
{
addEvent(linksElements[i], ‘mousedown’, recordClick, true);
if (! linksElements[i].getAttribute(‘id’) )
linksElements[i].setAttribute(‘id’,"link_" + i)
}
}
[/sourcecode]

Link tracking events
When a user clicks on a link the recordClick function fires. The first half of the function deals with differences in the event model and the DOM structure when trying to identify the source element. Once the link element has been identified the code exacts all the information required to make the Ajax call. The Ajax.Request object calls the passThrough function on the successful completion, but this is only used for
debugging.

[sourcecode language="javascript" wraplines="true"]
function recordClick(e)
{
if (typeof e == ‘undefined’)
var e = window.event;

var source;
if (typeof e.target != ‘undefined’)
{
source = e.target;
} else if (typeof e.srcElement != ‘undefined’) {
source = e.srcElement;
} else {
return true;
}

if (source.nodeType == 3)
source = source.parentNode;

var id, target, url, label

if( source.tagName == "IMG" )
{
if( source.parentNode.tagName == "A" )
{
id = source.parentNode.getAttribute(‘id’);
target = source.parentNode.getAttribute(‘href’);
}
label = source.getAttribute("alt");
}else{
id = source.getAttribute(‘id’);
target = source.getAttribute(‘href’);
label = source.childNodes[0].nodeValue;
}
url = document.location.href;

var pars = ”;
apiurl = "localhost/blog/api/addClick.aspx?id=" + id + "&label=" + label + "&target=" + target + "&url=" + url + "&rand="+Math.random();
ajaxRequest = new Ajax.Request(apiurl, {method: ‘get’, parameters: pars, onComplete: passThrough});
}

function passThrough( originalRequest )
{
//Helps debug api errors
//alert( originalRequest.responseText );
}
[/sourcecode]

Creating the link usage overlay
The link usage overlay is created dynamically. When the JavaScript first loads a keydown event is attached to the document body.

[sourcecode language="javascript" wraplines="true"]
addEvent(document, ‘keydown’, keyCheck, false);
[/sourcecode]

Whenever a key is pressed a check is made by keyCheck function. If the “Ctrl” and "X" keys are pressed and the overlay has not yet been created getClickThroughInfo function is called to collect the data using Ajax. The Ajax.Request object will then call the function displayClickThroughs on the successful completion. It loops through the XML and creates labels for each of the id’s it can match.

A simple display and hide mechanism is built into the keyCheck function to toggle show and hide the link labels once they have been created.

[sourcecode language="javascript" wraplines="true"]
function displayClickThroughs( originalRequest )
{
if (!document.getElementsByTagName) return false;

if( originalRequest.responseXml )
node = originalRequest.responseXml;
else
node = originalRequest.responseXML;

//Helps debug api errors
//alert( originalRequest.responseText );

if(node.childNodes[0].nodeType == 7)
rootNode = node.childNodes[1]
else
rootNode = node.childNodes[0]

for (var i = 0; i > rootNode.childNodes.length; i++)
{
linknode = rootNode.childNodes[i];
count = linknode.getAttribute(‘count’);
percent = linknode.getAttribute(‘percent’);
label = linknode.getAttribute(‘label’);
id = linknode.childNodes[0].nodeValue;

if ( document.getElementById(id) )
{
eltLink = document.getElementById(id);

eltDiv = document.createElement( ‘div’ );
eltDiv.className = "linklabel";
eltText = document.createTextNode( percent + "% – " + label );
eltDiv.appendChild( eltText );
document.body.appendChild( eltDiv );

ileft = parseInt(getPageOffsetLeft( eltLink )) + 10;
itop = parseInt(getPageOffsetTop( eltLink )) + 10;
eltDiv.style.left = ileft + "px";
eltDiv.style.top = itop + "px";
}
}
labelsCreated = true;
labelsDisplayed = true;
}
[/sourcecode]

The API

The API is made from two methods. The interface does not really follow the REST model. I need to find or build a good REST implementations for .Net. For the moment I have created two URLs against which you can make your method calls. The request should be made as a HTTP get request with a querystring of parameters.

If anyone is really interested I could include the .Net code and SQL Server scripts to recreate the API functionality. You can of cause use the following information to create your own API.

Recording a click through
The addClick method takes 4 parameters as a querystring “url”, “target”, “id” and “label”. The “url” is the location of the page containing the link. The “target” is where the link leads to and the “label” is the text displayed by the link. The parameters are returned for test purposes.

Successful addClick call returns

[sourcecode language="xml" wraplines="true"]
<?xml version=’1.0′ encoding=’UTF-8′ standalone=’yes’?>
<rsp stat="ok">
<url>www.glennjones.net/Post/804/UnobtrusiveJavaScriptandAjax.htm</url>
<id>link_100</id>

<target>www.glennjones.net/</target>
<label>Unobtrusive JavaScript and Ajax</label>
</rsp>
[/sourcecode]

Unsuccessful addClick call returns

[sourcecode language="xml" wraplines="true"]
<?xml version=’1.0′ encoding=’UTF-8′ standalone=’yes’?>
<rsp stat="fail">
<err code="100" msg="Request not complete" />
</rsp>
[/sourcecode]

Getting click through data for url
The getClicks method takes 1 parameter “url”. It returns either an “ok” or “fail”.

Successful getClicks call returns

[sourcecode language="xml" wraplines="true"]
<?xml version=’1.0′ encoding=’UTF-8′ standalone=’yes’?>
<rsp stat="ok">
<link percent="9" count="1" label="Unobtrusive JavaScript and Ajax">link_0</link>
<link percent="18" count="2" label="High quality dynamically resized images with .net">link_18</link>
<link percent="9" count="1" label="Home">Menu1</link>
<link percent="9" count="1" label="Archive">Menu33</link>
<link percent="36" count="4" label="Links">Menu34</link>
<link percent="9" count="1" label="About">Menu37</link>
<link percent="9" count="1" label="Technology">Menu40</link>
</rsp>
[/sourcecode]

Unsuccessful getClicks call returns

[sourcecode language="xml" wraplines="true"]
<?xml version=’1.0′ encoding=’UTF-8′ standalone=’yes’?>
<rsp stat="fail">
<err code="100" msg="Request not complete" />
</rsp>
[/sourcecode]

Example API link

www.glennjones.net/api/addClick.aspx?
id=menu37
&label=about
&url=www.glennjones.net/home/
&target=www.glennjones.net/about/

www.glennjones.net/api/getClicks.aspx?
url=www.glennjones.net/home/

Comments are closed.