tomhuda/metamorph.js — pull request #8 by nateps (2011-12-23 09:37:21+00:00)
Title: Switch from script elements to comments and comment elements

It required a series of crazy IE hacks, but I figured out how to make comments work in all browsers.

The advantages to comments are:
* They don't affect sibling CSS selectors, while script tags do. For example, `+`, `:first-child`, and `:nth-child()` can all be messed up by using script markers.
* DOM traversal methods that only operate on elements will ignore comment markers but not script markers
* HTML comments are valid anywhere, whereas script elements are not even though they work

First, regular comments can be pulled out quickly in IE9 and all other browsers via document.createTreeWalker. This method is actually equivalent to using document.createNodeIterator on comment nodes, because they can't have children. The advantage to createTreeWalker is that it is supported in old versions of Firefox (works in 2.0; didn't bother to test in anything older), whereas createNodeIterator wasn't implemented until Firefox 3.5.

IE 6-8, of course, is what makes this exciting. First of all, legacy IE supports the `` element that like other elements can have an id and can be retrieved via document.getElementById. However, the comment element acts entirely like an HTML comment from a parsing and rendering perspective. Thus, these elements don't affect CSS selectors. (The exception is :first-child in IE7, which does not properly ignore non-element nodes of any type.) The other problems are that IE doesn't seem to allow comment nodes to be children of select elements. However, the contents of the comment become a non-rendered textNode child of the select, so it is possible to special case this particular bug and just check the childNodes of all the select elements. Finally, IE has major issues with inserting "zero-scope" nodes, including comments, via innerHTML. I figured out that somehow putting `` keeps IE from stripping out comment elements that follow, and these empty script tags are removed even when in the middle of content inserted via innerHTML. I added a test to double check that case.

All tests passed in Mac Firefox 2.0, 3.0, 3.6, 9.0, current Chrome, Opera, Safari, and XP IE6, 7, 8, and Windows 7 IE9.