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 typeNode
, read only- The first child of this node. If there is no such node, this
returnsnull
.
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.