Thursday, March 26th, 2009

Querying the DOM on the Sly

Category: CSS, JavaScript, Library

>Remember when we thought there may be less CSS selector engines? :) Instead we have renewed performance-based competition among them.

Sly is the latest selector engine created by Harald Kirschner.

To use, it looks like this:

javascript
< view plain text >
  1. // Finds all odd rows in all tables
  2. var rows = Sly.search('table td:odd');
  3.  
  4. // Finds all links with class "internal" and an attribute "href" starting with "#".
  5. var links = Sly.search('a.internal[href^="#"]');
  6.  
  7. // Another notation is also possible, since Sly acts as a constructor
  8. var snippets = Sly('pre.highlight.javascript > code').search();
  9.  
  10. // features is just one element, lists has all list items are siblings of features
  11. var features = Sly.find('#features');
  12. var lists = Sly.search('~ ul', body);

What does it feature?

  • Pure and powerful JavaScript matching algorithm for fast and accurate queries
  • Extra optimizations for frequently used selectors and latest browser features
  • Works uniformly in DOM documents, fragments or XML documents
  • Utility methods for matching and filtering of elements
  • Standalone selector parser to produce JavaScript Object representations
  • Customizable pseudo-classes, attribute operators and combinators
  • Just 3 kB! (minified and gzipped, 8 kB without gzip)
  • No dependencies on third-party JS libraries, but developers can override internal methods (like getAttribute) for seamless integration.
  • Code follows the MooTools philosophy, respecting strict standards, throwing no warnings and using meaningful variable names

And it comes with speed tests:

Fork it at GitHub! :)

Posted by Dion Almaer at 11:37 am
33 Comments

++++-
4.2 rating from 57 votes

33 Comments »

Comments feed TrackBack URI

This is great for places where a full JS library like jQuery is overkill–for example, the iPhone. Until we see a stripped-down jQuery (removing all the stuff that accommodates IE), alternative selector engines will be great for the iPhone.

Comment by Nosredna — March 26, 2009

Full image. Browsers are cut of, so from bottom to top:
IE6 (nicknamed: the big blue), IE7, IE8, FF3, Opera, Safari 4, Chrome 2

Comment by digitarald — March 26, 2009

amazing! competition always leads to better performance and code. I like that.

Comment by Aimos — March 26, 2009

When I run the test jQuery and Sly showed same score on FF3.1 and Safari.
Instead of jQuery I think in that test should be Sizzle.

Comment by Kas — March 26, 2009

Nice to see that it’s smaller than Sizzle. We recently added Sizzle to our code base but it make our script size increase and each version of Sizzle seems to add on to this size. But I guess the bug fixes etc in Sizzle comes with a price, don’t know how well tested this Sly engine is yet so who knows it might become big as well.

Comment by Spocke — March 26, 2009

@Spocke: if you don’t have to worry about the code quality and quantity of your selector engine it’s worst the extra bytes. :D

Comment by Aimos — March 26, 2009

@Aimos: Ha ha, yeah. I think I stick with Sizzle since quality is more important than size. And so far Sizzle has been working like a charm. :)

Comment by Spocke — March 26, 2009

Wow there is a lot of CSS selectors now! Sly, Sizzle, MochiKit.Selector, if only they were organized rather then scattered around the web then there would be true competition. How many people do you think really know about Sly.js??

Comment by jhuni — March 26, 2009

Congratulations Harald Kirschner! Great work!

@Spocke: “I think I stick with Sizzle since quality is more important than size.” What makes Sizzle that much better than Sly?

Comment by davidwalsh83 — March 26, 2009

@Kas tests call jQuery.find, which is an alias for Sizzle. Also those 2 browsers have querySelectorAll, use ff3.0/opera/ie6/7 or the “dom fragment” case for non-qsa results.

@Spocke I see more selectors fail in jq than in sly (check Simple selector list, or even DOM fragment case) but I’m also going to add selector unit tests from jq and prototype to ensure stable quality and to show sample implementations. And especially in tinymce speed/portability should be important, since selectors can be a bottle neck in huge documents.

@jhuni: It was release today, but I can give you visitor stats tomorrow ;)

Comment by digitarald — March 26, 2009

@jhuni: Well, now that it has been exposed here on Ajaxian I think a bunch of devs know about it ;)

@davidwalsh83: Because hype==quality?

Comment by rasmusfl0e — March 26, 2009

p.s. @Spocke: no external unit tests are added yet, but sly uses something better: Specs runner. Linked it in my blog post, but it not got in this article.

Comment by digitarald — March 26, 2009

@davidwalsh83: I haven’t compared Sizzle with Sly but I’m always suspicious when something is super small. Have they just skipped fixing various browser quirks most of the time the code is there for a reason. Users have complained that our editor is huge compared to other editors but thats because the other editors doesn’t bother to add workaround for browser issues. I don’t know if that’s the case with Sly but I suspect it due to the size difference.

@digitarald: Performance is important, but browser consistently and stability is more important. It doesn’t matter how fast something is if it doesn’t work or if it fails to work tomorrow. I like the concept of feature detection that Resig applies to jQuery and Sizzle this method needs more code but it will make the code more resilient for API changes to the browsers. I don’t see any of that code in Sly, is that something that will be added?

Anyway, I think it’s important to have alternatives to the Sizzle engine. Competition is good for development. So keep up the good work. :)

Comment by Spocke — March 26, 2009

@Spocke

I personally never had a problem with the overall combined size of TinyMCE – the only thing I dislike about it (and it really doesn’t matter most of the time) is the insane amount of time it take to upload it over FTP (especially within WordPress). That’s do more to the sheer number of files, combined with the inefficiency of FTP for such a large number of tiny files, and not necessarily the total file size.

I agree with most of that you posted btw. :-)

Comment by CaptainN — March 26, 2009

in the tests I just ran my selector engine was a little faster on average across the browsers, and a little more accurate in some cases.

I’m a bit suspicious about the marketing of CSS selector engines, and the accompanying nth% faster that X library claims — it’s more hype than sizzle half the time.

Comment by PeteB — March 26, 2009

Wasn’t I reading somewhere that there may be up to 10-15ms discrepancy in JS timers that used traditional Date objects? Seems a bit odd when looking at these Slickspeed tests in terms of 0.8 ms or 1.2 ms.

Regardless, I enjoyed the headline. :)

Comment by zachleat — March 26, 2009

That’s why I didn’t give you the “xy% faster” talk ;) . The graph can tell one story, and the one linked below it tells another one.

You can get the slickspeed suite I used from the github repo, linked in the article, so you can check against DOM Fragments and XML Documents (which are imo too often ignored in tests but yet important to support).

@Spock: “browser consistently and stability is more important”. That is why I worked first on the specs and added the slickspeed tests later. The full feature/quirks detection is still missing and therefore the next coming task. I’ll also exclude more pseudo selectors into additional files, so the core file keeps slim.

Comment by digitarald — March 26, 2009

not to nit pick, but there are a couple of issues here:

* the performance tests should be using Dojo 1.3rc2, not 1.2.3
* similarly, both JQuery 1.2.6 and 1.3.x should both be included
* for fairness, the tests should test Acme and Sizzle’s *stand alone* versions, not the versions that wrap returns in different types (dojo.NodeList and the JQuery object). Otherwise the tests are apples-to-oranges
* the times of most of the tests are below browser resolution on the test harness that’s reporting these numbers. That’s just flat-out dishonest benchmarking
* stacking results like this to show “aggregate times” isn’t useful, and is indeed deeply misleading

Some of these things are the fault of the source benchmark suite, some of them are the author’s own lack of experience with benchmarking. They should all be fixed, though. The numbers, as presented, are meaningless.

Regards

Comment by slightlyoff — March 26, 2009

I can honestly say there has never been a query I’ve come across that I couldn’t solve with getElementById, getElementsByTagName, and getElementsbyClassName (native or otherwise). Extra bytes sure, but so is a CSS selector engine. It just seems like an awful lot of overkill for nothing more than a little syntactic sugar, especially with querySelectorAll just around the corner.

Comment by RyanMorr — March 26, 2009

@slightlyoff Thanks for your review, here my comments:

* Dojo has good results, what’s the arguing about? I tested with ACME and the results were very similar.
* List of frameworks is long enough, this is not a jQuery comparison. If you need one, you can find on in their 1.3 release announcement.
* jQ test uses jQuery.find, which is an alias for Sizzle.
* Selectors run 10 times, so the results are totally super honest, I swear! If you want the responsible persons for the small bars in the graphs, ask the awesome browser developers.
* No, I love them! Those colourful graphs with stacked times, very informative and you can still see browser differences. Maybe you are confused because they are cut off, you have to open the image to see them fully. I’m just hoping that google graphs soon adds a glas or 3d effect, maybe gradients.

Numbers ran on a slickspeed setup that was also used to measure for other framework releases, with most used selectors on “real-life” template. To contribute, fork the speed test suite and share your benchmarking wisdom, I’m looking forward to it.

Comment by digitarald — March 26, 2009

Your shoul also check YASS js-library.

Comment by pepelsbey — March 27, 2009

@RyanMorr,

The value of a selector engine probably depends on the application. For something relatively light weight in the first place, you[r users] probably won’t notice the difference in performance, and you probably won’t see your code get out of control. If you’re doing anything particularly heavy, you’ll start to notice the performance hit right away. And it’s clear from your comment that you aren’t using particularly complex selectors (which is only the tip of the iceberg for selector engines, but I digress), but it’s not safe to assume others aren’t.

A project I’m working on right now, for what it’s worth, relies heavily not just on selectors that would be a pain in standard DOM selection, but on caching and well-engineered performance that I’d be hard-pressed to build for myself in the same timeframe without the support of a library (in this case, I’m supporting both jQuery and Prototype).

Comment by eyelidlessness — March 27, 2009

@slightlyoff: is the wealth of experience with benchmarking supposed to smell like bad sportsmanship, or is that just when posting on Ajaxian? :)

Comment by sixtyseconds — March 27, 2009

Could anybody explain why “div[class]” test gives different result for Mootools Slick (59 instead of 55) in FF 3.0.7? Is it a bug or something?

Results are also different among libraries for p:contains(selectors), p:only-child div[class!=made_up]

Comment by kazukin — March 27, 2009

I don’t see a point in all these comparisons of selector engines against each other. I used the provided benchmark with the different browsers on hand and all it told me is it depends on the browser. I use jQuery so that’s what I was comparing Sly against.

Everything sucked in the IE variants except for IE8 which shows marked improvement.

Not much difference between the two in most other browsers. Sure, Sly did better in most but the differences tended to be around 10ms. I don’t know about you but I can’t tell the difference between 20ms and 30ms all that well.

Everything absolutely screamed in Chrome and Safari4 and jQuery beat Sly by a small margin each time. It was actually fun doing the benchmark in Chrome just to watch how fast it ripped through the test.

Therefore, I am left to conclude that if the selector engine is efficient in its task then its performance is heavily influenced by the browser. And if all browsers will eventually be as fast at Javascript as Chrome then the selector engine you choose won’t matter a whole lot.

Comment by travisalmand — March 27, 2009

Not much difference between the two in most other browsers. Sure, Sly did better in most but the differences tended to be around 10ms. I don’t know about you but I can’t tell the difference between 20ms and 30ms all that well.

Now, perform several similar operations in a matter of a single function, and watch 20ms vs 30ms become 200ms vs 300ms or 1000ms vs 1500ms.

Users do notice, and care about, speed.

Comment by eyelidlessness — March 27, 2009

Imho performance and consistently go hand in hand, both beeing important for a good user experience and less developer frustration.
More and more quality in both, permanently improving by competition,
that’s what we all have to go for. Keep it going! =o)

Comment by Thasmo — March 27, 2009

digitarald:

I think you didn’t understand the point at all: your tests aren’t measuring what you think they are. Running tests 10 times tells you *nothing* if the total accumulated test time is below the resolution of the system timer you’re using to measure with. And with Slickspeed on most browsers, they are. The version of Slickspeed I used to develop Acme against runs each test for at least 1s, not a fixed number of times, and reports # of queries/ms, not time to run a fixed number of operations. Your numbers aren’t measuring what you think they are.

Regards

Comment by slightlyoff — March 27, 2009

@slightlyoff: when is the last time you ran the same query selection on the same DOM context more than 10 times, in real life? :)

Comment by sixtyseconds — March 27, 2009

@sixtyseconds:

I can’t speak for slightlyoff, but the point isn’t that running the same query N times is “real world”, but that because of limitations of the ECMA 262 implementation of most browsers (save Chrome), measuring the number of executions in a defined time gives you a more accurate measurement of the performance of the function itself. This translates indirectly to real world use in the sense that you won’t run the same query N times, but that you’ll run dozens of queries in a given setting, and that the real world speed of each of those queries affects cumulative speed of the application generally.

I’m really shocked that this needs any comment at all. Of course benchmarks are somewhat removed from “real world” use; the purpose is to test real world methods discretely and accurately, to identify general and specific performance boons and bottlenecks.

Comment by eyelidlessness — March 27, 2009

sixtyseconds:

most browsers (save Chrome, as eyelidlessness said) don’t have fine-resolution timers. That means that if your function runs in 0.9 ms and mine runs in 0.01ms, they both report as “0ms”. The point of doing enough iterations is to show what heavy usage of the selector engine will *really* look like in relative terms. Cumulative numbers (like slickspeed generates at the bottom) aren’t good for anything else anyway.

The objection is about methodological accuracy. Slickspeed is b0rked in this regard, but pumping up the # of revs gives you a simple way to address the timer resolution issue. NOT doing it simply obsfucates actual comparative performance.

Regards

Comment by slightlyoff — March 27, 2009

@travisalmand Testing in browsers that have the querySelectorAll is of course more fun, but *real* engine results come in the other browers or when you use the “DOM Fragment” case (where qsa doesn’t work). And yes, JavaScript performance depends on the browser …

@slightlyoff/eyelidlessness: Benchmarks can be computations/time or time/computation, telling that one of these patterns would be totally useless blow it out of all proportions. But henceforward I will also check the numbers with computations/ms. (gotta say thanks for sharing your benchmarking wisdom, as I asked for it)

I also did not add the latest acme (but dojo) because it returned several 0-results (for simple id selectors) and made the results useless (so much I learned about benchmarks in school, but I’m still trying to find my old notes). Maybe you can point me to a working version. Sizzle was not that different, in comparison to jQuery, since my jq tests already used jQuery.find (alias for Sizzle).

@sixtyseconds: Using a selector more than 10 times (or more) in a short period of time would have various common reasons. First of all is event delegation, which is more and more used (selectors are matched against a bubbling event). Think also about ideas like LiveQuery from jq, which queries the dom in a short interval for new elements. The same would happen when you use a simple query like “.title” in a loop on a list of elements. Today, many developers (usually programmer background) also overuse selectors, without thinking of the bottle neck situations.

Comment by digitarald — March 27, 2009

Benchmarks can be computations/time or time/computation, telling that one of these patterns would be totally useless blow it out of all proportions.

It’s not that one method is useless, it’s that it’s impossible to measure accurately in any browser but Chrome, and even then only to a resolution of 1 ms. In contrast, measuring operations per second gives you a great deal more accuracy, and also a great deal more distinction between sub-ms timing. Something that runs 0.01 ms takes 1/90 the time of something running 0.9 ms (using slightlyoff’s example), but are reported identically in a ms/op scenario. Obviously the distinction in this case is minute… until you run thousands of like operations in one go.

If the information gleaned from benchmarks is important, then the information gleaned from the more accurate benchmark is more important than otherwise.

Comment by eyelidlessness — March 29, 2009

Leave a comment

You must be logged in to post a comment.