I scope, you scope, we all scope for NoScope! JS style element injection quirks in IE

The other day I was writing some updates and improvements for Modernizr, one to detect for generated content support and two to improve stylesheet and element injection. Modernizr already in a few places inserts a stylesheet and a corresponding element to do some tests e.g. generatedcontent, touch, css3transforms and a few others. All this happened multiple times; each test would inject an element and an inline style element, do its test then remove both elements. All this happens while the page is loading and as you can see the more tests that involve these steps exponentially grow the number of times it needs to touch the DOM.

Modernizrs’ previous method of injecting the elements revolves around forking for IE, IE<9 uses addRule() rather than insertRule(), this makes it hard to write a simple re-useable method for doing such a test that is cross browser. So I began thinking how this could be re-written to be simplified for re-usability. Rather than inject a style element into the head of a document we could use innerHTML and inject into the element that Modernizr will test against.

elem.innerHTML = "<style>"+css+"</style>";

Simple, easy and certainly re-usable. But something that stumped me for a while is IE6-9 would not append the style element inside the test element?

IE and the case of the NoScope element

After searching high and low I found some useful information about scope and NoScope elements in IE. Basically IE has a few elements that are considered NoScope which when trying to insert with innerHTML will be stripped! On this infamous page however it mentions if you precede a NoScope element with a scoped element they will not be stripped and we can then insert our style element.

elem.innerHTML = scopeElem+"<style>"+css+"</style>";

Huzzah! It works in IE6-9. We now have a simpler easier way to inject an element with associative styles and we’ve halved our touching of the DOM by bundling the test element and style element into one appendChild operation.

What’s considered a scoped element?

A scoped element can be anything other than a style, script or comment element. In Modernizrs’ case it uses the soft hyphen entity (&shy;) so as not to interfere with measurements done on the injected element. We could very well prepend the innerHTML string with the test element we wish to inject, but for easier referencing I create it in the method and pass it to the callback. This way we can keep the reference to the node and not have to query the DOM again.

injectElementWithStyles = function(rule,callback){
    var style, div = document.createElement('div');
    style = ['&shy;','<style>',rule,'</style>'].join('');
    div.id = mod;
    div.innerHTML += style;

    ret = callback(div,rule);

There’s also a few other additions that centre around the injectElementWithStyles() method such as the test_bundle() method which does the DOM injection test in one fell swoop. If you want to know more go check out the source or hit me up on twitter.

[link href=”http://cssn.in/ja/033″]