No worries, I’m not gonna post review #213 about what CrazyEgg.com does. I think there are already enough of them out there, a few good ones:
Solution Watch: Crazy Egg Launched – Visualize Visitor Clicks
Bloghelper: Crazy Egg = Click Tracking + Heat Maps for Your Blog
Devlounge: A Sneak Preview at Crazyegg!
Techcrunch: See What Your Website Visitors Are Doing With Crazy Egg
When I saw CrazyEgg.com the first time, I was all engineer and what I wanted to know was, how the hell do they do this?
<script type="text/javascript">
//<![CDATA[
document.write('<script'+'
src="http://crazyegg.com/pages'+'
/scripts/4382.js?'+(new Date()).getTime()+
'" type="text/javascript"></scr'+'ipt>');
//]]>
</script>
The above code is the only thing one needs to include into the page. Translated this leeds to this:
http://crazyegg.com/pages/scripts/4382.js?1234567
I the file I downloaded in raw form is here. Not very readable, but after some formating I have a better version here.
A little bit later and I had the version below. Now I hope most of the names make sense and help understand the code:
var _cep=[];
var _ceL={};
var _ceLT=new Date();
var _ceET=null;
var _ceAID=new Array();
//creates a unique id for elements that have no id tag
function CreateUniqueID(_1)
{
var _2=false;
var _3="";
for(i=0;
i<_ceAID.length;i++)
{
if(_1==_ceAID[i].name)
{
_2=true;
break;
}
}
if(_2)
{
_ceAID[i].count+=1;
count=_ceAID[i].count;
}
else
{
_ceAID.push({name:_1,count:1});
count=1;
}
return (_1+count);
}
//We wanna have a good name for all the
//elements that we have on the heatmap
//afterwards.
//An anchorElements element is either a Link
//or an Image
function CreateNameForElement(anchorElement)
{
imgCollection =
anchorElement.getElementsByTagName("img");
//check if the object that the user clicked on is
//an image, if yes, we can either use the alt
//tag, the title or some other tags.
if(imgCollection.length>0)
{
img=imgCollection[0];
if(img.alt)
{
link_text=img.alt;
}
else
{
if(img.title)
{
link_text=img.title;
}
else
{
if(img.longDesc)
{
link_text=img.longDesc;
}
else
{
link_text=img.src;
}
}
}
}
//if it is not an image, lets see if we can use
//the inner text or a title or something
else
{
if(anchorElement.innerText)
{
link_text=anchorElement.innerText;
}
else
{
if(anchorElement.text)
{
link_text=anchorElement.text;
}
else
{
if(anchorElement.title)
{
link_text=anchorElement.title;
}
else
{
link_text=anchorElement.href;
}
}
}
}
if(link_text=="")
{
link_text="No Text Found";
}
return link_text;
}
//checks if the links points to a different site
function IsSameSite(linkHostname,
locationHostname)
{
same=(linkHostname.indexOf(locationHostname)>=
|| locationHostname.indexOf(linkHostname)>=0) ?
true : false;
return same;
}
//for all pics on the page we set a link to
//crazyegg.com
//for links, we replace them with links to
//crazyegg.com
//QUESTION? not sure how they track html controls
//like buttons?
function PlaceLinksToCrazyEgg()
{
var anchorElements =
document.getElementsByTagName("a");
for(var i=0; i<anchorElements.length; i++)
{
anchorElement = anchorElements.item(i);
// QUESTION? looks like we do not replace
//absolute links?
if(anchorElement.href.indexOf("http")==0)
{
link_id="CEID_";
link_text="";
if(anchorElement.id=="")
{
link_id +=
IsSameSite(anchorElement.hostname,
location.hostname) ?
anchorElement.pathname :
nchorElement.href;
link_id = link_id.replace(/W/g,"_").
link_id = link_id.replace(/_+/g,"_");
link_id = link_id.replace(/_$/,"");
link_id = CreateUniqueID(link_id);
}
else
{
link_id = anchorElement.id;
}
link_text =
CreateNameForElement(anchorElement);
_cep.push(
{
"text":link_text,
"href":anchorElement.href,
"target":anchorElement.target,
"id":link_id,"name":anchorElement.name
});
var _9="?";
_9+="pid=83913&";
_9+="token=124678787&";
_9+="text="+escape(link_text)+"&";
_9+="href="+escape(_7[i].href)+"&";
_9+="element_id="+escape(link_id);
anchorElement.oldHref=
anchorElement.href;
anchorElement.href=
"http://crazyegg.com/track/"+_9;
FixStatusBar(anchorElement);
}
}
}
//this way the status bar still shows the old url and
//not to crazyegg.com
function FixStatusBar(_a)
{
var _b=_a.onmouseover;
if(typeof _a.onmouseover!="function")
{
_a.onmouseover=ShowLinkInStatusBar;
}
}
function ShowLinkInStatusBar()
{
window.status=this.oldHref;
this.onmouseout=function()
{
window.status="";
return true;
}
return true;
}
// QUESTION? No idea what this does
function ceHC(_c)
{
var _d="";
_d+="pid=83913&";
_d+="token=124678787&";
_d+="text="+_ceL.text+"&";
_d+="href="+escape(_ceL.href);
}
// QUESTION? No idea what this does
function ceMoveOn()
{
switch(_ceL.target)
{
case "": self.location=_ceL.href;
break;
case "_new": ceNewWindow=
window.open(_ceL.href);
break;
case "_blank": ceNewWindow=
window.open(_ceL.href);
break;
}
}
PlaceLinksToCrazyEgg();
Code formatted by Code Formatter Plugin for Windows Live Writer.
Code from above in Text format here, it’s easier to read.
The result of this exercise:
- They replace all links with links to crazyegg.com and append an unique id. This is how they can track on which links visitors click. Makes sense.
- They also add links to pictures. Very cool, never even thought of how many visitors helplessly click on my unlinked pics.
What I cannot figure out are the following things:
- How do they forward the post to my own webserver afterwards? I don’t think a simple redirect would work or does it? Even for POST commands?
- How do they track clicks on Form elements? (They do, if you check a heatmap with Input elements for example).
Maybe the ceMoveOn() function does have something to do with this….
Anyone out there that solved the puzzle?