A Javascript CSS3 Selector and Matcher not using XPath
Ongoing development and current work is now on GitHub: NWMatcher Sources
NWMatcher 1.3.8.js is the latest update on Google Code. It is highly suggested to upgrade to this version.
Source code, add-ons and unit tests can be downloaded from GitHub, there are both Prototype and jQuery selector unit tests.
Version 1.3.8 is the current release, all known features and bug fixes are included. Snapshots of newer versions will be available on GitHub !
The caching system has been currently moved out of core code as an add-on so it can be included optionally.
Many bugs have been fixed to make this work even on older Safari 2.0.x and Mozilla 1.7.
How to include the NWMatcher selector engine in your web pages
For desktop browsers
<script src="nwmatcher.js"></script>
For headless environment
<script src="nwmatcher-noqsa.js"></script>
Straight to the TESTS
These are some of the tests hosted here:
- CSS3 Selector tests and sibling / descendant tests
- official Prototype 1.6.1 selector test unit (current)
- official jQuery 1.3.2 selector test unit (current)
Following tests are hosted on my own server:
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 tool.
Despite being a W3C CSS specification, "document order" was not implemented in most frameworks/libraries until 2009.
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. Some unit tests in Prototype and jQuery had to be modified to account for "document order".
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.
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).
NWMatcher provides the following public methods:
// return a true / false boolean
// if the selector matches element
NW.Dom.match( element, selector );
// return an array of elements and
// apply a callback to each element
NW.Dom.select( selector, context, callback );
// configure optional engine functionality
NW.Dom.configure( { OPTION: true/false, ... } );
// wrapped/fixed DOM methods
NW.Dom.getAttribute(element);
NW.Dom.hasAttribute(element);
NW.Dom.byId(element, context);
NW.Dom.byTag(element, context);
NW.Dom.byName(element, context)
NW.Dom.byClass(element, context);
NW.Dom.contains(ancestor, descendant);
// method to test compilation
NW.Dom.compile(selector, mode);
// methods to add operators / selectors
// and engine related function resolvers
NW.Dom.registerOperator(symbol, resolver);
NW.Dom.registerSelector(name, regexp, resolver);
Of these methods, the most importants are the select()
and the match()
methods, respectively to retrieve collections
of elements or check that a single element matches a specific selector.
Most of the time you would probably just need to use these two.
The default configuration settings should match the needs of most applications,
however the configuration options can be changed independently and programmatically
by invoking the NW.Dom.configure()
method and passing a configuration
object made up of enabled/disabled options keywords:
// disable querySelectorAll branch and only use a
// working cross-browse branch, precise but slower
NW.Dom.configure( { USE_QSAPI: false } );
// enable complex :not() queries (not in W3C specs)
NW.Dom.configure( { SIMPLENOT: false } );
// display console warnings instead of throwing errors
NW.Dom.configure( { VERBOSITY: false } );
// reset all options keywords to default state
NW.Dom.configure( {
SIMPLENOT: true,
VERBOSITY: true,
USE_QSAPI: true
} );
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 18Kbytes, only 6Kbytes 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, 2.x, Mozilla 1.7+, Netscape 7.2+
- Internet Explorer 5.5, 6, 7
- Safari/Webkit 2.x, 3.x
- Chrome 5.x, 6.x, 7, 8
- Konqueror 3.4.x
- Opera 7.2, 8.x
Newer browsers
- Firefox 3.x, 4.x and Gecko engines
- Safari/Webkit 4.x, 5.x
- Internet Explorer 8, 9
- Chrome 9, 10.x, 11.x
- Konqueror 3.5, 4.x
- Opera 9.x, 10, 11
Mobile browsers
- iOS 3.x, 4.x (Apple iPhone, iPod, iPad)
- Android 1.x, 2.x, 3.x (Google Phones)
- Playstation PSP/PS3 (Sony)
- Symbian 8.x 9.x (Nokia)
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 (@samleb) - syntax suggestions, code cleanup and patches
Tobie Langel (@tobie) - adapter, testing and integration in the Prototype library
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
Jeff Watkins (Apple Store) - for being the first to include NWMatcher in Coherent JS (Apple Store framework)