Hacking the Technorati Badge or: How I Hacked Document.Write() in Order to Make it Work With application/xhtml+xml
I’ve reworked this code to make it unobtrusive and consequently lighter. See “Hacking the Technorati Badge part Deux” to read about it.
The first thing I noticed when I switched to application/xhtml+xml was how unforgiving the XML parser is with badly formed markup. It stops parsing and displays a big fat red error message where your page is supposed to be. The second thing I noticed was how my nifty little third party JavaScript includes all stopped working. I’m talking of course about my AdSense ads and my Technorati Badge (which is now back in my footer thank-you-very-much).
Looking into the mystery disappearance I found that I was getting a bunch of JavaScript errors like this:
uncaught exception: [Exception... "Object cannot be created in this context" code: "9" nsresult: "0x80530009 (NS_ERROR_DOM_NOT_SUPPORTED_ERR)" location:...
Putting two and two together I realized that “Object cannot be created in this context” and “ERROR_DOM_NOT_SUPPORTED” meant that JavaScript’s document.write() method wasn’t supported in the XML DOM.
Well, after a few frustrating evenings of trying to write a string-to-html-DOM-nodes parser in JavaScript (yes, I can be that dumb sometimes) and a few philosophical discussions with Mathieu, I finally gave up. That is until he gave me the key to fixing this problem. You see, I got pretty far in my string-to-html-DOM-nodes parser work. The problem wasn’t that. The problem I was having–and what Mathieu so deftly solved–was in identifying where the document.write() was occuring. Maybe it would help at this point if I told you that my solution to getting document.write() working in application/xhtml+xml was to override the document.write() method and redirect it through a function of my own. That way I could manage what it did and essentially force it to play nice instead of crapping out. Every time I referred to this in my override code I just got the document object returned to me, rather than the DOM node that the script was being run in. (You know what I mean, when Technorati or AdSense tells you to just plop their ready-to-go, cut-and-paste <script> tag into your page.) So what, you may ask was Mathieu’s solution? A global variable set to the current script tag’s parent node’s ID. That way, the document.write() override could just grab that node and do its work inside it.
Then of course I also realized that I could replace my tedious, potentially error prone, and ultimately overkill code with a simple .innerHTML instead. That’s right, the non-standard workhorse .innerHTML which is no worse than using the W3C’s DOM methods (thanks PPK). So even though the purist in me was screaming for me to use my recursive beast of a node creator, the minimalist in me said “what are you crazy? Just use .innerHTML“.
Long story short (too late for that huh?) here’s the code. Oh and there’s an added function in there to clean up Technorati’s broken markup. Yes, you heard me, it’s broken.
The JavaScript:
var Utils = {
DWID : "",
DW : function(str){
var el = document.getElementById(Utils.DWID);
str = Utils.CleanTechnorati(str);
el.innerHTML = str;
},
CleanTechnorati : function(str){
return str.substring(0, str.indexOf("»")+5) + ";" + str.substring(str.indexOf("»")+5, str.length);
}
}
document.write = Utils.DW;
The XHTML:
<script type="text/javascript">Utils.DWID = "technorati_include";</script> <li id=”technorati_include”><script type=”text/javascript” /></li>
So basically, every time you’ve got a script you want to include, you just change the value of the global variable just before and assign it the script’s parent element’s ID and you’re all set! Enjoy!
Sphere: Related Content6 Comments
Sorry, the comment form is closed at this time.



May 2nd, 2006 at 12:33 pm
[...] In my previous post on Hacking the Technorati Badge I told the tale of how I managed to get it working with the application/xhtml+xml MIME type. Since then I tweaked the technique and managed to get rid of the extra <script> tag immediately preceding the XHTML element you want badge to appear in. I was able to do this by identifying the word “technorati” in the src attribute of the <script> tag and then inserting the contents of the badge immediately next to it. [...]
May 8th, 2006 at 11:19 am
Brilliant! I wonder if you can also then “defer” the script so it runs onload or at the end of the page (and thus does not slow down the initial display of the page).
May 8th, 2006 at 11:31 am
Patrick: I’m working on a way to do that because right now the script only works for single document.write()s. As soon as you have multiple writes in a script my “fix” bombs. I was thinking of building an output string and dumping it every time I encounter a new <script> tag, but I’m having a bit of a hard time processing it while the page is loading. I’ll figure something out I’m sure. :-)
May 12th, 2006 at 1:16 am
[...] My first attempt only worked with one script tag. My second attempt wasn’t really an attempt at all. I took an idea I had, quickly typed it into my editor, saved and saw it working fine (on a page with just one script tag in it). I soon realized that it was really bad code and disabled it. [...]
May 12th, 2006 at 8:29 am
To solve the “script only works for single
document.write” problem, why not append?
el.innerHTML += str;
But of course even if you fix there broken html, there is
always the problem that they can change there source and
broke it again without a warn.
May 12th, 2006 at 9:14 am
name is required: Good call, but I think the parser may complain if you write only a part of a nodeset to the DOM and wait for the next document.write to complete it (hence the stack I coded in the latest version of the script which gathers all the pieces and then inserts them in one shot).
Also, about the broken html–unfortunately that’s life. This whole script is a “fix” for a bad practice to begin with. Deciding to use application/xhtml+xml means that your markup has to be perfectly valid and unfortunatley when you import markup from other sources you can’t always rely on them to validate. This goes for everyone using these JavaScript includes. The only difference is that for me (and anyone else using real XHTML) the parser bombs when there’s a problem with the markup instead of just ignoring it. Thus the need for vigilance. But I’m not too worried. I told Technorati about their broken markup a while ago and they still haven’t done a thing about it. So I doubt things will be changing anytime soon. ;-)