Working Around Omniture
The most recent site I developed at work had a couple of pages with lots of what would have once been called DHTML functionality, stuff like tabbed interfaces, modal windows and the like. In development, this stuff all worked really well, and I even did my level best to make it all accessible. But just before we deployed, we had to incorporate code from Omniture that tracks users as they move through a site. Omniture is widely used script for tracking the path users follow through sites. And here we ran into some problems.
The first problem was that Omniture’s Javascript code is heavily obfuscated and optimized. All of the function names and variables use extremely brief and cryptic names, using one or maybe two characters, something that makes it extremely difficult to debug when you have a problem. Unlike an open source Javascript library like, say, jQuery, Omniture doesn’t provide a development version of their library with reasonably named functions and variables, so if you’ve got a problem, you’re stuck trying to figure out what function a
and its variables x, y and z, are doing and what they’re stomping on. And the stomping on is another problem; if you incorporate another minified library, like, say, the minified version of jQuery, some of those single-character-named functions or variables are going to conflict with the single-character-named functions or variables in Omniture’s code. This could have been avoided if Omniture namespaced their code so it doesn’t stomp on anything else and blow up your site (can you tell I’ve seen this happen more than once?) When we discovered this conflict, we wound up going with a slightly larger version of jQuery that doesn’t create those functions and variables with the same names as the ones Omniture creates.
But the bigger problem we found is that Omniture’s code is slow. Verrrrrrrry slow. On a tracked link, the Omniture code would do whatever it does (hard to tell what, exactly, because of the obfuscated code), and only when it was done would the default action of the link be triggered. So when you clicked on a tab in the tabbed interface, or on a link that would open up a modal window, links that were pretty much instantaneous on the development network and still extremely speedy on the staging server now took forever to activate, on the order of three or four seconds. This was really unacceptable from a user experience point of view, but from the client’s perspective, the links had to be tracked. What to do? We could change the cursor to an hourglass and then change it back, but the link would still be slow to activate. Not good.
Omniture provided two possibilities for tracking links; the first was to add an id as part of a query string at the end of a link. This caused us problems in some cases, such as the tabs, because now links that were within a page and wouldn’t cause the browser to reload the page, with the addition of the query string, now appeared to the browser to have different parameters from the originally loaded page, and so it would reload the page, bouncing users back to the default tab. That didn’t work, so we tried their alternate syntax, which involved assigning one of their functions to the onclick event handler. That led to the above-mentioned slowing to a crawl.
In a conversation with the Omniture expert at work, I asked if it mattered if the id associated with a link were actually called by the page or if it could be called by another URL. The answer led to a solution. The id could be added to any URL, even, say, a 1×1 spacer GIF. (Finally, a justifiable use for a spacer GIF in modern code!) So we could make the onclick event handler call /images/x.gif?iid=page_identifier_here
. Even better, we could call that URL asynchronously using Ajax. Since we were already using jQuery, all we had to do was to add a line like the following to the already existing (unobtrusively linked) onclick event handler:
$.get(’/images/x.gif?iid=page_identifier_here’);
This would send the call to the server that included the required Omniture tagging, but since the call was being executed with jQuery’s Ajax get()
function, the call was happening asynchronously, and the default actions could be triggered without waiting for a response. That restored the speedy user experience. But we found that once the asynchronous call was finished, whatever URL was in the href
attribute would then reload, causing, for example, the tabs to once again bounce back to the default. So we had to define a callback function to be executed upon the completion of the Ajax request. Since we didn’t want anything to happen at the completion of the request, the function is a simple return false
:
$.get(’/images/x.gif?iid=page_identifier_here’,function(){return false});
Et voila, the client gets the tracking they require, and the user gets the experience they deserve. No longer does Omniture cause the interface to bog down.
If you’re using any library other than jQuery, it’s bound to have a similar function for asynchronous Ajax calls. If you’re rolling your Javascript from scratch, such a call would look something like this:
function trackme(url,query) { // Native XMLHttpRequest object if (window.XMLHttpRequest) { request = new XMLHttpRequest(); // ActiveX XMLHttpRequest object } else if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); } if (request) { request.onreadystatechange = function() { return false; }; request.open("get", url, true); request.send(query); } }
And then your onclick event handler would include something like this:
onclick="trackme(’file.html’,’iid=page_identifier_here’); return false;"
The return false
here is required to prevent the onclick from executing the default action of the href even before the Ajax request is returned.
Tags: omniture ajax jquery cleverosity
Posted at 10:36 PM
I feel your pain. The distinguishing client at MCD has a healthy mixture of Google Analytics, Omniture and Visual Science (pre Omniture buy-out).
I think you approached this issue the right way. A few months ago, I hacked together a wrapper for VS which did something similar, just w/o the Ajax. I took an old-school approach and just instantiated a new Image object and set the src property. In hindsight, I’m not confident that it’s asynchronous. If not, I think some refactoring is in order :)
Good post.
Posted by Mike Girouard at 8:32 PM, July 27, 2008 [Link]