Operation is not supported" code: "9

July 08, 2009

My Adventures with Lift, XHTML and Google Analytics

Yesterday I decided to add google analytics to my website. I thought this endeavor would take no more than half an hour. I'm embarrassed to say that it ended up taking me over 4 frustrating hours. I ran into 2 stumbling block, both of which were related to the fact that I was trying to use google analytics on an XHTML page.

It started out simply enough. I opened an account and was then told to add this snippet to my page. I dutifully pasted the code into the base page and refreshed my website. I immediately got an error.

Problem 1: IE6 - Error: Syntax error

My first stumbling block was an IE6 error message that simply said "Error: Syntax error". This seemed like a classic copy/paste mistake, so I view the source to make sure the snippet was correct. It turned out that each quote character (") in the GA snippet had been encoded as ", which meant code like this:

var foo = "more foo";

turned into:

var foo = "more foo";

which naturally caused a syntax error.

I've ran into this behavior many times, so instantly knew the problem. I built my page on the Lift framework and as part of its page rendering process it HTML encodes all text nodes. I simply added //<![CDATA[ and //]]> to the beginning and end of each GA script tag, and the problem went away.

Problem 2: Firefox - Operation is not supported" code: "9

My next stumbling block was with Firefox. I reloaded my page and again, an error immediately appeared. The error message from firebug simply said "Operation is not supported" code: "9". As you can tell, this error message was not very helpful. I was completely lost until I stumbled across an old post which explained that XHTML does not support document.write() and that best thing to do is to manually import the google analytics source file with a script tag. I implemented the suggestion and everything worked beautifully. This is actually the technique I use on my personal site. Unfortunately this is not a universal solution.

Potential Problem 3: HTTPS - Unsecured content warning

I did not personally run into this last problem, but if you are running google analytics on a secured page, the GA snippet you use must be smart enough to dynamically switch from a HTTP request to a HTTPS request. The original snippet used document.write() to achieve this, but it turns out XHTML does not support document.write(). The easiest way to get around this is to use the native DOM API to dynamically create a script tag with the appropriate protocol and append it to the page's head.

I've also noticed that the default snippet creates a global variable named pageTracker. As far as I can tell, this variable is not used anywhere. So it seems that the ideal snippet would avoid adding this global variable. That said, removing pageTracker is somewhat risky, since it's possible that another services (adsense?) uses pageTracker.

It's unfortunate that google analytics didn't simple work right out of the box. Hopefully one day the google analytics teams will fix these compatibility issues. Then again, there might be a very good reason they implemented it the way they did. If you have any idea why, please let me know!

Original Snippet
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
try {
var pageTracker = _gat._getTracker("UA-XXXXXXX-X");
pageTracker._trackPageview();
} catch(err) {}</script>
Non-secure Snippet: I use this version on my site
<script src="http://www.google-analytics.com/ga.js" type="text/javascript"></script>
<script type="text/javascript">
//<![CDATA[
    try { var pageTracker = _gat._getTracker("UA-XXXXXXX-X"); pageTracker._trackPageview(); } catch(err) {}
//]]>
</script>
Ideal Snippet
<script type="text/javascript">
//<![CDATA[
    var _g = document.createElement('script');
    _g.type = "text/javascript";
    _g.src = ((location.protocol == "https:") ? "https://ssl." : "http://www.") + "google-analytics.com/ga.js";
    document.getElementsByTagName('head')[0].appendChild(_g);
    try { _gat._getTracker("UA-XXXXXXX-X")._trackPageview(); } catch(err) {}
//]]>
</script>