The Ashes

Technology, Science and other news
August 31, 2009

Creating a querySelector for IE that runs at “native speed”

Posted by : admin
Filed under : General

Hello Ajaxians, my name is Paul Young and I am the co-founder of Skybound Software. We’re the company behind Stylizer, which is a real-time CSS editing tool. We’re taking a pretty radical approach to CSS editing, and as such, a lot of what I do is “web technology research”, which is looking for better ways of doing things with a web browser, ultimately so that Stylizer can automate more of the web development process. This has allowed me the chance to discover a few things that have had significant impact on a web developer’s workflow, as was the case with State Scope Image Replacement.

In conducting some research for a future version of Stylizer I found a way to create a querySelector method for IE 7 and lower. querySelector is a very useful feature of newer web browsers. It takes a CSS selector as string parameter and returns an array containing the HTML elements that match the selector. Unfortunately it doesn’t work in IE 7 and lower… until now 🙂

My querySelector technique works in IE7 back to IE4 (although I only tested it back to version 6). It’s only 327 characters minified, and runs ultra-fast because it doesn’t parse strings or traverse the DOM. Keep in mind though that it’s using the CSS selector engine built into the browser, which means no funky CSS 3 selector features will work, as they weren’t implemented old versions of IE.

In order to explain how it works, I’ll need to first cover something else I discovered: Single Execution CSS Expressions (SECEs). These are an enormously powerful technique that unfortunately has been made impossible in IE8. We’re using these extensively in the built-in CSS reset feature in Stylizer to eradicate bugs and to add critical but missing features in IE 6 and 7.

About CSS Expressions

The problem with IE’s CSS expressions feature is that because they’re recalculated hundreds of times per second, they’re notoriously slow. Fortunately, I’ve found a way to ensure they’re only executed once per matched element. Examine the code below:

PLAIN TEXT
CSS:

  1.  
  2. DIV { -singlex: expression(this.singlex ? 0 : (function(t) { alert(t.tagName); t.singlex = 0; } )(this)); }
  3.  

There are 3 things you need to know to understand how this works:

  • CSS expressions are executed regardless of whether the CSS property name is valid. So “-singlex” is just an arbitrary name.
  • CSS expressions are executed on every matched element. So in the case above, on every div on the page.
  • Inside a CSS expression, the “this” keyword refers to the current matched HTML element.
  • The code above checks to see if the matched element has a property called “singlex”. If it does, it just returns 0. Otherwise, it executes an inline function, passing it a reference to the matched element. Inside that function, we can perform whatever processing we want. At the end, we set a flag on the element to ensure that the function won’t be executed again.

This is how you can do complex processing inside a CSS expression without having to worry about a performance hit. The function only executes on the first execution of the expression, and then on every subsequent execution, 0 is returned instantly. The performance cost of running an if test and returning 0 is negligible, even if you’re doing it thousands of times per second. If you paste that CSS into a page with 3 div elements on it, in IE it will alert “DIV” 3 times.

The things you can do with SECE’s are basically limited only by your imagination. Here is an example of using one to fake the CSS content property in IE 7 and lower:

PLAIN TEXT
CSS:

  1.  
  2. /* Newer browsers */ DIV:after { content: “Generated content!”; } /* IE 7 and lower */ DIV { -singlex: expression(this.singlex ? 0 : (function(t) { t.innerHTML += “Generated content!”; this.singlex = 0; } )(this)); }
  3.  

You can use SECEs to fake all sorts of things like generated content, min-width and min-height, CSS counters, CSS outlines, and more. But most importantly, you can…

Create a querySelector method with Single Execution CSS Expressions!

Creating a querySelector method is just a matter of dynamically adding a SECE to a page that copies a reference to each matched HTML element into a globally accessible array, and then returning that array. Examine the code below:

PLAIN TEXT
JAVASCRIPT:

  1.  
  2. /*@cc_on if (!document.querySelector) document.querySelector = function(selector) { // Add a new style sheet to the page var head = document.documentElement.firstChild; var styleTag = document.createElement("STYLE"); head.appendChild(styleTag); // Create a globally accessible element array document.__qsResult = []; // Create the SECE that copies all matched // elements to document.__qsResult. styleTag.styleSheet.cssText = selector + "{qs: expression(this.__qs?0:(function(t){document.__qsResult.push(t);t.__qs=0;})(this));}"; // Reflow the page. Without this, the SECE won’t execute. window.scrollBy(0, 0); // Clean up and return head.removeChild(styleTag); return document.__qsResult; } @*/
  3.  

There you have it! The function is wrapped in a conditional compilation comment to make sure only IE 7 and lower can see it. Here is a minified version for convenience:

PLAIN TEXT
JAVASCRIPT:

  1.  
  2. /*@cc_on if(!document.querySelector)document.querySelector=function(s){d=document;h=d.documentElement.firstChild;t=d.createElement("STYLE");h.appendChild(t);d.__q=[];t.styleSheet.cssText=s+"{x:expression(this.__q?0:(function(t){document.__q.push(t);t.__q=0;})(this));}";window.scrollBy(0, 0);h.removeChild(t);return d.__q;}@*/
  3.  

I hope you can make use of SECEs and this querySelector method. Maybe it even has a place in jQuery? I don’t know. (John Resig, are you reading? 🙂

UPDATE

After thinking this through a little further, I realized that you don’t need to use a SECE at all for this, just a CSS expression added with JavaScript will do. Also, one of the commenters (Jordan1) pointed out that the code wouldn’t return elements that had already been queried once. I’ve posted an update to the code below that should rectify the issue:

PLAIN TEXT
JAVASCRIPT:

  1.  
  2. /*@cc_on if (!document.querySelector)
  3.         document.querySelector = function(selector)
  4.         {
  5.             var head = document.documentElement.firstChild;
  6.             var styleTag = document.createElement("STYLE");
  7.             head.appendChild(styleTag);
  8.             document.__qsResult = [];
  9.            
  10.             styleTag.styleSheet.cssText = selector + "{x:expression(document.__qsResult.push(this))}";
  11.             window.scrollBy(0, 0);
  12.             head.removeChild(styleTag);
  13.            
  14.             var result = [];
  15.             for (var i in document.__qsResult)
  16.                 result.push(document.__qsResult[i]);
  17.             return result;
  18.         }
  19.     @*/
  20.  

NOTE: Fancy writing a guest post for Ajaxian? Got some tips on content that we haven’t covered? Please email us, or tweet me 🙂

Tags :

No Comments

(required)
(will not be published) (required)
(opitional)

cciash.com EN ES IT DE PT CZ FR RU
June 2017
M T W T F S S
« Sep    
 1234
567891011
12131415161718
19202122232425
2627282930  

Pages

Categories

Resources

There are many online poker site where you can play but at poker.hk you can play the poker games with all the knowledge you need related to the game with the poker school available in both the English and Chinese language.

Super Casino

Now you can bet on any sports and any sporting events from all the comfort from your home. Bet770 allows you to bet on any events and match with in just 3 clicks. They also offers great odds on football betting for every premier and champions league match. Get £50 free in bets when you register.

Bingo770, offering best online bingo games with £7.70 free no deposit Bonus!