Supertext Home
Chief of the System Blog

CrazyEgg.com – How does it track Visitor Clicks?

September 6th, 2006 by

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?

Related Posts

  1. WordPress URL Rewrite for IIS
  2. Print Screen replacement for Vista
  3. Do Follow
  4. DotNetKicks Plugin for WordPress and Live Writer

Leave a Reply

  • Topics
  • Archive
  • Subscribe
  • Facebook
  • Twitter