Quantcast
Viewing all articles
Browse latest Browse all 10

Adventures With document.documentElement.firstChild

Here’s an interesting DOM test-case I ran across inadvertently yesterday.

For the purpose of this post assume the following markup:

< !DOCTYPE html>
<html>
<!-- i broke the dom -->
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
	<title>Testcase</title>
</head>
<body>
<p>Something</p>
</body>
</html>

If I use document.documentElement.firstChild I don’t get consistent behavior. In Firefox and IE I get the <head/> element, which is what I was initially expecting. In WebKit (Safari/Chrome) and Opera. I get the HTML comment which I wasn’t.

I think WebKit and Opera are technically correct on this as the DOM Level 2 specs state:

firstChild of type Node, read only
The first child of this node. If there is no such node, this
returns null.

A COMMENT_NODE is a node and therefore should have been first. As for the position of the comment, the document is valid HTML5 and also is valid as XHTML 1.0 Strict and HTML 4 Strict. My interpretation is that this means indeed the comment is the first valid node in the documentElement.

One of the reasons why I even thought to use document.documentElement.firstChild is that I saw Google doing it the other day for the new asynchronous tracking code for Google Analytics (currently in beta). Originally the code was:

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXX-X']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script');
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 
        'http://www') + '.google-analytics.com/ga.js';
    ga.setAttribute('async', 'true');
    document.documentElement.firstChild.appendChild(ga);
  })();

It has now been updated to prevent this problem. I don’t know if I was the first to report it or if it was already known by the Google engineers. The code, still in beta is now:

  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-XXXXX-X']);
  _gaq.push(['_trackPageview']);

  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(ga);
  })();

The new code seems a bit more resilient. They also got rid of the longhand ga.setAttribute in favor of just ga.async and added the type attribute.

There is a test case for anyone who wants to try it. I haven’t found a relevant Mozilla bug.


Viewing all articles
Browse latest Browse all 10

Trending Articles