Is jQuery traversal preferred over selectors?
There's no cut and dry answer to this, but with respect to the :last
selector you're using, it's a proprietary extension to the Selectors API standard. Because of this, it isn't valid to use with the native .querySelectorAll
method.
What Sizzle does is basically try to use your selector with .querySelectorAll
, and if it throws an Exception due to an invalid selector, it'll default to a purely JavaScript based DOM selection/filtering.
This means including selectors like :last
will cause you to not get the speed boost of DOM selection with native code.
Furthermore, there are optimizations included so that when your selector is very simple, like just an ID or an element name, the native getElementById
and getElementsByTagName
will be used, which are extremely fast; usually even faster than querySelectorAll
.
And since the .last()
method just grabs the last item in the collection instead of filtering all the items, which is what Sizzle filters normally do (at least they used to), that also will give a boost.
IMO, keep away from the proprietary stuff. Now that .querySelectorAll
is pretty much ubiquitous, there are real advantages to only using standards-compliant selectors. Do any further filtering post DOM selection.
In the case of $("#vacations").find("li")
, don't worry about the interim results. This will use getElementById
followed by getElementsByTagName
, and will be extremely fast.
If you're really super concerned about speed, reduce your usage of jQuery, and use the DOM directly.
You'll currently find notes in the docs for selectors like :last
, that warn you about the performance loss:
Because :last is a jQuery extension and not part of the CSS specification, queries using
:last
cannot take advantage of the performance boost provided by the native DOMquerySelectorAll()
method. To achieve the best performance when using:last
to select elements, first select the elements using a pure CSS selector, then use.filter(":last")
.
But I'd disagree that .filter(":last")
would be a good substitute. Much better would be methods like .last()
that will target the element directly instead of filtering the set. I have a feeling that they just want people to keep using their non-standards-compliant selectors. IMO, you're better of just forgetting about them.
Here's a test for your setup: http://jsperf.com/andrey-s-jquery-traversal
Sizzle, jQuery's selector engine, parses the string with regex and tries to speed up very basic selectors by using getElementById
and getElementsByTagName
. If your selector is anything more complicated than #foo
and img
, it'll try to use querySelectorAll
, which accepts only valid CSS selectors (no :radio
, :eq
, :checkbox
or other jQuery-specific pseudo-selectors).
The selector string is both less readable and slower, so there's really no reason to use it.
By breaking the selector string up into simple chunks that Sizzle can parse quickly (#id
and tagname
), you're basically just chaining together calls to getElementById
and getElementsByTagName
, which is about as fast as you can get.