querySelectorAll on old IE versions: something that doesn’t work
In today’s post, I’m going to show an interesting technique to solve a problem and then I will tear it to pieces and explain why it is actually useless. I believe that negative results should also be published so that we can save other people from wasting time trying the same thing. So here goes…
A few days ago, a post on Ajaxian proposed a new version of a somewhat old technique to implement querySelectorAll on old versions of IE, using the browser’s native CSS engine. That sounds like a great idea at first, and the hack is quite clever. The idea is to dynamically add a CSS rule to the document that has the selector that you want to evaluate, and an expression that adds the matched elements to a global array.
When I read this, it reminded me of a similar approach that I had tried a few years ago. At the time, we were considering implementing our own selector engine (we had not yet decided to integrate jQuery to our Ajax offerings, which in the end made the whole effort moot) so we explored a number of approaches.
My idea was different in that it doesn’t use expressions at all. It does dynamically create a style rule, but instead of an expression, it just sets a non-existing “foo” style property to the equally arbitrary value of “bar”. It then scans the whole document (using the much decried and IE-specific but very fast document.all) and gets the computed style for each of the elements. We then look for the foo property on the resulting object and check whether it evaluates as “bar”. For each element that matches, we add to an array.
Here’s the code:
(function() { var style = document.styleSheets[0] ||
document.createStyleSheet(); window.select = function(selector) { style.addRule(selector, "foo:bar"); var all = document.all, resultSet = []; for (var i = 0, l = all.length; i < l; i++) { if (all[i].currentStyle.foo === "bar") { resultSet[resultSet.length] = all[i]; } } style.removeRule(0); return resultSet; } })();
or, in minimized form:
(function(){var d=document;var a=d.styleSheets[0]||
d.createStyleSheet();window.select=function(e){
a.addRule(e,"f:b");var l=d.all,c=[];
for(var b=0,f=l.length;b<f;b++)if(l[b].currentStyle.f)
c[c.length]=l[b];a.removeRule(0);return c}})()
That’s 235 characters, which is not too bad (although not quite #twitcode small).
The first problem with that approach though is that because it’s using the native CSS selection engine in IE, it has the same limitations and quirks. That means no fancy CSS 3 (or even 2) selectors. It also means any IE bug will surface into the result set.
In other words, if you want more selectors than that, you will need to parse the selector string and branch off the code to another, more complete engine whenever something not supported is used. It also means that you need to know what is supported and what isn’t. That could be done through some dynamic discovery but doing so, we are getting into much complexity.
So limited as it is, how does it perform?
I ran the code in a SlickSpeed test (where I removed the selectors that it couldn’t handle) on IE6 and the good news is that despite the document.all scan and the current style computation, it’s more than three times faster than Paul Young’s implementation that got featured on Ajaxian.
But the bad news is that it’s also six times slower than jQuery:
I’m afraid a hack to use the native CSS selection engine of the browser is always going to be slower than an optimized pure JavaScript implementation (to be clear, I’m not talking about native implementations of querySelectorAll, but about hacks such as this which try to surface the feature on older IE versions that don’t have querySelectorAll). Somewhat counter-intuitive, but true.
End of story. Just use jQuery. :)