How is the jQuery selector $('#foo a') eva

2019-01-21 05:09发布

问题:

As a example of jQuery code (https://coderwall.com/p/7uchvg), I read that the expression $('#foo a'); behaves like this:

Find every a in the page and then filter a inside #foo.

And it does not look efficient.

Is that correct? And if yes, how should we do that in a better way?

回答1:

That is correct - Sizzle (jQuery's selector engine) behaves the same way as CSS selectors. CSS and Sizzle selectors are evaluated right-to-left, and so #foo a will find all a nodes, then filter those by nodes that descend from #foo.

You improve this by ensuring that your leaf selectors have a high specificity, usually by giving them a class or ID.



回答2:

how should we do that in a better way?

Use the context parameter from jQuery.

$('a', '#foo');

Now jQuery will search all anchors within the context of the element with id: foo.

In your query the context is defaulted to document when omitted:

$('#foo a'); == $('#foo a', document); 

In this case, your query is indeed not efficient.

You might take a look at this article.



回答3:

While it is true that Sizzle is a right-to-left engine (which is the same way css is interpreted), it is not true that the specific selector in your example would select all anchor elements on the page and then filter their parents to match the id of "foo". Sizzle actually optimizes any selector that starts with an ID and uses that as the context for the entire selection, rather than using the document. In other words, the selector you've chosen basically translates to:

document.getElementById("foo").getElementsByTagName("a")

Really, that's not a bad selector at all.

However, given the other things jQuery needs to do (which includes looping over the elements to merge them onto the jQuery instance), jQuery("#foo").find("a") will always be the fastest because jQuery implements a jQuery object creation shortcut for id-only selectors, and then it does the find rooted from #foo.

In other words, Sizzle itself is not much different when doing Sizzle("#foo a") and Sizzle("a", document.getElementById("foo")), but jQuery("#foo").find... will be faster because of jQuery's own ID shortcut.

By the way, my remarks on Sizzle is assuming querySelectorAll is not being used. If it is, Sizzle just passes it on to qsa, which still isn't as fast as using jQuery's ID shortcut.



回答4:

You can use find() for more granular control on your selector order:

$('#foo').find('a');

This will of course be more impressive with more complex selectors, where you can chain find() and filter().

For the record $('#foo').find('a') === $('a','#foo')

[Update] ok, I realized later that it's exactly what your link says...

The jQuery selector engine (Sizzle) has been refactored last year, you'll find detailed explanations here: http://www.wordsbyf.at/2011/11/23/selectors-selectoring/



回答5:

Instead of filtering with a inside #foo elements, simply attach a class to a elements and get a elements with class like $("a.class");. This would be more efficient.



回答6:

Yet another "try it for yourself":

  1. jsperf for various selectors on 10000 elements
  2. jsperf for various selectors on 300 elements
  3. jsperf for various selectors on a "more representative DOM"

Doesn't seem to be much difference with a "flat" DOM (1 & 2), but the performance varies much more with a nested DOM.

Also note that some of the test cases aren't selecting the correct elements (i.e. $('.a') vs $('.a', context)), but I left them from the original tests just for comparison.



回答7:

This example will retrieve the all anchors elements a in an element called foo, to Find every a in the page and then filter a inside #foo as you want u should select a #foo

$("a #foo");

this will retrieve all the foo elements inside a elements.