Today I was revising some older javascripts to match my XHTML 1.1 code. In case you're not yet aware, JavaScript's document.write method is not allowed in well-formed XHTML pages - as described at W3C in XHTML FAQ:

Because of the way XML is defined, it is not possible to do tricks like this, where markup is generated by scripting while the parser is still parsing the markup.

However, document.write is used everywhere so if you still rely on some older non-XHTML-aware scripts I'll show you how to give your legacy code a DOM-facelift.

I'll take the code from Rating@Mail.ru counter as an example. It is used on the majority of Russian websites but look at the quality of this code:

          <!--Rating@Mail.ru COUNTER-->
<script language="JavaScript" type="text/javascript"><!--
d=document;a='';a+=';r='+escape(d.referrer)
js=10//--></script><script language="JavaScript1.1" type="text/javascript"><!--
a+=';j='+navigator.javaEnabled()
js=11//--></script><script language="JavaScript1.2" type="text/javascript"><!--
s=screen;a+=';s='+s.width+'*'+s.height
a+=';d='+(s.colorDepth?s.colorDepth:s.pixelDepth)
js=12//--></script><script language="JavaScript1.3" type="text/javascript"><!--
js=13//--></script><script language="JavaScript" type="text/javascript"><!--
d.write('<a href="http://top.mail.ru/jump?from=341994"'+
' target=_top><img src="http://top.list.ru/counter'+
'?id=341994;t=84;js='+js+a+';rand='+Math.random()+
'" alt="Rating@Mail.ru"'+' border=0 height=18 width=88>'+'<'+'/a>')
if(js>11)d.write('<'+'!-- ')//--></script><noscript><a
target=_top href="http://top.mail.ru/jump?from=341994"><img
src="http://top.list.ru/counter?js=na;id=341994;t=84" 
border=0 height=18 width=88
alt="Rating@Mail.ru"></a></noscript><script language="JavaScript" type="text/javascript"><!--
if(js>11)d.write('--'+'>')//--></script>
<!--/COUNTER-->

        

The only word to describe such code is: BAD! There is no way it's going to be validated as anything more strict than HTML 4.01 Transitional, and even then it is plain ugly. Let's beautify it and replace document.write (this example uses d=document;d.write() construct for short) with something that will validate as pure XHTML 1.1.

First, numerous <!-- and --> comments will have to go - they give my JavaScript console in Firefox the creeps. Second, the lines starting with if(js>11)d.write seem redundant - what they do is, basic`ly, escape the <noscript> block if the code is rendered by JavaScript engine of certain version, which is omitted automatically if JS is available, anyway! And while we're in the mood for cleaning, let's remove target property of <a> tag and border from <img>. And then we shall double-quote the rest of properties and put the final slash (”/”) in <img> tag.

One more thing: the path in <img src="..."> is unique but <a href="http://top.mail.ru/jump?from=341994"> part is static and thus appears twice - in <script> and in <noscript> - for no good use. We will move it outside, thus saving on code length and avoiding redundancy. Here is what we shall get:

          <!--Rating@Mail.ru COUNTER-->
<a href="http://top.mail.ru/jump?from=341994"><script language="JavaScript" type="text/javascript">
d=document;a='';a+=';r='+escape(d.referrer)
js=10</script><script language="JavaScript1.1" type="text/javascript">
a+=';j='+navigator.javaEnabled()
js=11</script><script language="JavaScript1.2" type="text/javascript">
s=screen;a+=';s='+s.width+'*'+s.height
a+=';d='+(s.colorDepth?s.colorDepth:s.pixelDepth)
js=12</script><script language="JavaScript1.3" type="text/javascript">
js=13</script><script language="JavaScript" type="text/javascript">
d.write('<img src="http://top.list.ru/counter'+
'?id=341994;t=84;js='+js+a+';rand='+Math.random()+
'" alt="Rating@Mail.ru"'+' height="18" width="88" />')
<noscript><img
src="http://top.list.ru/counter?js=na;id=341994;t=84" height="18" width="88" 
alt="Rating@Mail.ru" /></noscript></a>
<!--/COUNTER-->

        

This code certainly looks much better - so if you're not interested in full XHTML 1.1 compatibility and just need the corrected code for Mail.ru counter, well, there you go. And we shall move on too.

I used the code shown by Mark Pilgrim in his article, The Road to XHTML 2.0:

          if (document.getElementById) {
  var l=document.createElementNS("http://www.w3.org/1999/xhtml","link");
  l.setAttribute("rel", "stylesheet");
  l.setAttribute("type", "text/css");
  l.setAttribute("href", "/css/js.css");
  l.setAttribute("media", "screen");
  document.getElementsByTagName("head")[0].appendChild(l);
}

        

The code above puts a reference for CSS-file into document's <head>:

          <link rel="stylesheet" type="text/css" href="/css/js.css" media="screen">

        

What we need is to create a similar line for our <img> tag. Here is the code identical to the one above:

          if (document.getElementById) {
 var i=document.createElement("img");
 i.src="http://top.list.ru/counter?id=341994;t=84;js="+Math.random()+js+a;
 i.width=88;
 i.height=18;
 i.alt="Rating@Mail.ru";
}

        

But it lacks one critical part, the one with childAppend, which will put our tag in its place. However, we'll have no use of the <img> inside the <head>. We might get something like this:

          document.getElementsByTagName("body")[0].appendChild(l);

        

Note however that this way the tag is going to be rendered at the very bottom of the page which is not exactly our intention.

But we use a different method instead of document.getElementsByTagName - document.getElementsById! And keeping in mind that we now have an anchor tag around the <script> block we will give it a unique ID and then point appendChild at it:

          <!--Rating@Mail.ru COUNTER-->
<a href="http://top.mail.ru/jump?from=341994" id="m">
<script language="JavaScript" type="text/javascript">
d=document;a='';a+=';r='+escape(d.referrer)
js=10</script><script language="JavaScript1.1" type="text/javascript">
a+=';j='+navigator.javaEnabled()
js=11</script><script language="JavaScript1.2" type="text/javascript">
s=screen;a+=';s='+s.width+'*'+s.height
a+=';d='+(s.colorDepth?s.colorDepth:s.pixelDepth)
js=12</script><script language="JavaScript1.3" type="text/javascript">
js=13</script><script language="JavaScript" type="text/javascript">
if (d.getElementById) {
var i=d.createElement("img");
i.src="http://top.list.ru/counter?id=341994;t=84;js="+Math.random()+js+a;
i.width=88;i.height=18;i.alt="Rating@Mail.ru";
d.getElementById("m").appendChild(i);
}</script>
<noscript><img src="http://top.list.ru/counter?js=na;id=341994;t=84" 
height="18" width="88" alt="Rating@Mail.ru" /></noscript></a>

        

Congratulations, we now have a valid XHTML-friendly JavaScript! The result can be seen in action on my sidebar.

Update: this script is not, in fact, XHTML Strict - see the following article, " XHTML-friendly Javascript: I stand corrected" for valid code.