A Javascript CSS3 Selector and Matcher not using XPath
NWMatcher 1.1.1.js is the latest update on Google Code, it is at SVN r71 (20090516). It is highly suggested to upgrade to 1.1.1
NWMatcher development and sources can be downloaded from GitHub, they contain both Prototype and jQuery selector test units.
Version 1.1.1 is the current release since all the features and bug fixes are now included. Freezed 1.1.1 is available on Google Code SVN !
The caching system have been reworked to avoid DOM manipulation slow down when using Mutation Events.
Many bugs have been fixed to make this work on older Safari 2.0.x and Mozilla 1.7.
Straight to the TESTS
A modified SLICKSPEED TEST allowing for variable number of iterations.
The original SLICKSPEED TEST courtesy of the Mootools team.
Numbers doesn't always show the truth, however great job.
Testing NWMatcher with official Prototype 1.6.0.3 selector test unit.
Testing NWMatcher with official jQuery 1.3.2 selector test unit.
Despite being a W3C CSS specification, as of today, "document order" is not implemented in most frameworks/libraries.
NWMatcher has been the first selector engine to implement "document ordered" result sets. This is really important to extract document TOC's or serialize submitted form fields. Currently, it seems the only other selector engine implementing "document ordered" is Dean Edwards Base2 (that I am aware of) Some unit tests in Prototype and jQuery had to be modified to account for "document order".
Updates: it seems that John Resig has finally added this to jQuery 1.3.2, Sly 1.0rc from Harald Kirschner was also fixed to return "document ordered" result sets.
Why another one ?
A very long story, to be as short as possible I needed faster and more compact code to:
Test if the element match the criteria written as CSS selector.
At that time most frameworks/libraries only provided a similar approach, too slow for my needs:
Go through all matching elements and see if element is there.
Some time stolen to my main project and some extra sleepless nights allowed me to finally "close the circle" and publish a quite capable Event Manager that is now using NWMatcher to its full potential for event delegation (as was initially planned).
Is there something new ?
Sure there is. You may have not noticed yet, but as an extra to what standard selector engines offers, by coupling NWEvents and NWMatcher you get methods that let you solve the "onload" problem most times. Look here for more info on this new way to handle the problem: This is something like Behaviors, but events can be attached earlier, no need to wait "onload" and only one event is attached for each group of elements and future dynamically inserted elements if they match the group criteria (selector).
When I started this, there where few choices fast enough for my needs, one was the very basic getElementsBySelector() written by Simon Willison the other one was CSSQuery by Dean Edwards. After testing both engines I realized that speed was a big problem and should left all the goodies of Dean CSSQuery and just stay with the simpler one. Less CSS selectors compatibility but at least with Simon getElementBySelector() I could easily setup a proof of concept and see it working, slowly on the worst cases but still working.
The final touch up
In the last years I have been caught so much by the unobtrusive web programming and started to implement the "Event Delegation" pattern in the most common and raw way in Javascript. About mid 2007 this was working just well for most CSS2 selectors, but I wished a boost in performance and I wanted to have more CSS2 and newer CSS3 selector to be also available for event delegation. I asked for help to Peter Michaux. He patiently listened to my ideas, debated with me on that and provided me a first skeleton of compiled selectors.
Event Delegation applied to Javascript is the fastest and more scalable pattern for any type of Javascript application, from the smallest scriptlets to complex frameworks up to the biggest RIA applications. Web UI responsiveness can only be greatly improved if we can count on fast and stable events and delegates.
NWMatcher aims to be one of the building stones of this scenario and NWEvents is the door to event delegation using reliable events combined with fast and reliable selectors.
This is by no means the last word on this, my code can be improved both in syntax and speed, there are surely bugs still around that I will try to correct with help of testers.
Testing NWMatcher with and without caching
NWMatcher provides a way for the user to configure the state of it's internal caching system, ENABLED or DISABLED. Even if we tell NWMatcher to do caching, it will still do it's best to detect the DOM has changed by using Mutation Events. This is true on all browsers but IE, so the caching system is disabled by default on IE. You may enable it using setCache().
By default the cache is "enabled" on Firefox, Safari (Webkit bug #8191),
Opera, Konqueror, this gives the best results in speed still being
able to be notified and flush the cache when the document changes.
Important details on how to use it
NWMatcher has been written with compatibility in mind. To achieve that goal I avoided to use too many native methods, this also due to the many bugs that affect the standard DOM methods especially on Internet Explorer (ex: getElementById, getElementsByTagName).
The singleton provides only four usable methods. They are:
// return boolean true/false
NW.Dom.match(element, selector);
// return array of elements
NW.Dom.select(selector, from);
// enable document cache, no return value
NW.Dom.setCache(true, document);
// flush document cache, no return value
NW.Dom.expireCache(document);
Of the four methods, you would probably just need to use two, the "select()" and the "match()" methods, respectively for getting collections or just checking a single element.
The caching can be set programmatically by calling:
// pass true to enable or false to disable
NW.Dom.setCache(false, document);
The current cache can be forced to expire by calling:
// no return value
NW.Dom.expireCache(document);
Objectives for this code:
- Standard compliance to published specs
- ensure result sets are "document ordered"
- CSS3 support (including "of-type" pseudos)
- correct resolution of case-sensitivity in attributes
- singleton, no dependencies, few JS1.2 native functions
- no XPath used just loops and one getElementsByTagName
- not extending anything, not adding to objects or arrays
- minified code 6Kbytes, only 3Kbytes if served compressed
- multi-frame/multi-document aware, fully cross-browser compatible
Tested compatibility
Some testing have been done on different browsers and platform OS (Linux/Mac/Windows) on phones and palm devices. I haven't found browsers failing completely yet, some have weak UNICODE support (Opera and Safari 2), I will keep the list updated with incoming info.
Older browsers
- Firefox 1.5+, Mozilla 1.7+, Netscape 7.2+
- Internet Explorer 6
- Safari/Webkit 2+
- Konqueror 3.4.3
- Opera 7.2, 8.0
Newer browsers
- Firefox 2, 3, 3.5 and Gecko engines
- Safari/Webkit 3.0, 3.1, 3.2, 4beta
- Internet Explorer 7, 8
- Konqueror 3.5, 4.0
- Opera 9+, 10
Bugs ?
Yes there are. Well hidden though, couldn't find them all yet...Want to help ?
Credits
Peter Michaux Peter Blog - initial code template for compiled CSS selectors
Samuel Lebeau (Xilinus) - Prototype adapter, syntax suggestions and patches
E. Leith (LavaScript) - bug reports, suggestions and great debugging in his library
John-David Dalton (@jdalton) - several patches and the idea of including a sensitivity map
Ken D. Snyder (PUlp) - for including NWMatcher in his own PUlp framework project
Jeff Watkins (Apple Store) - for being the first to include NWMatcher in Coherent JS (Apple Store framework)