tags:

views:

38

answers:

3

Hi,

The title is a bit confusing but here's what I'm after:

  • I have a set of elements containing all the h3 elements in a page.
    In jQuery terms: var mySet = $('h3').

  • I also have an div element var myContainer = $('div#foo')

  • I need to find all items in the set mySet that are children of myContainer.

Any ideas? I bet there's some magical one liner for this but I cannot think of any. I'd rather not loop through every element in the set manually and use .closest(myContainer) or something like that to determine the relationship.

Note that in my scenario, I cannot use a new selector like $('div#foo h3') (that would be too easy) as I don't have access to the actual selector values. So it has to be dynamic.

A: 

I don't know about jQuery but in YUI3 nodelists returned from a selector query contain the actual selector string used to create the list, perhaps jquery exposes similar functionality?

unomi
Thanks for the downvote. The question explicitly asked to not loop through the sets. I am not sure if it is worth making the distinction of a forEach vs filter...
unomi
+2  A: 

You can use the .filter method to reduce the elements to those that match a specific selector:

mySet.filter("div#foo > *");

You can also get the selector used in a jQuery object by accessing the .selector property.

Andy E
You don't always have a selector, you might have a collection: `$('div > :hidden').add('p').filter(':odd')` - I doubt you'll get a correct selector there, though I haven't checked.
Kobi
Just a note, filter would exactly loop through all the elements of (in this case) mySet, albeit not _manually_.
unomi
@unomi: Very few jQuery selectors and DOM traversal methods are free of loops. The ones that don't involve loops are the ones that can attain the elements using `querySelectorAll()`, which isn't supported in older browsers. The point is that `.filter()` handles the looping *and* the condition checking for you.
Andy E
+1  A: 

Interesting. Assuming you have two jQuery collections and you don't know the selectors:

var myContainer = $('div');
var mySet = $('h3:even');

filter seems to work:

myContainer.children().filter(mySet)

Keep in mind, however, that this is undocumented as far as I can see, so it may change.
.not can also accept a collection of elements, which works similarly.

Working example: http://jsbin.com/owuru

Kobi
As would this loop through myContainer.children, and compare each one to (worst case) each of mySet.
unomi
@unomi - this is essentially intersecting two sets, so yes, you have to go through both. In terms of complexity, it could be done in `nlogn`, and this is probably `n²`. Note that I did not assume there's a single parent.
Kobi
All I meant with my answer is that it is possible that the initial selector is actually available, as Andy E confirmed - but as you later stated it is possible that they are not necessarily 'pure' selector generated lists, it renders my suggestion moot. If performance is a concern you should likely ensure that smallset.filter(largeset) and optionally remove hits from the largeset.
unomi
@Kobi I am not quite sure what you meant by "Note that I did not assume there's a single parent."
unomi
Sorry, I meant that `myContainer` may have more than a single element, "parent" is the wrong word here. I also use `.children` as a helper - My answer is really about intersecting two sets, which starts one step after `.children` was called. I see it as `var set1 = $('selector1'); var set2 = $('#id').children(); var result = set2.filter(set1);`
Kobi
Yes, I think that is a good general solution.
unomi
Thanks, this worked fine for me and filled my criteria :) Even though this is undocumented, it seems to work in the current jQuery version which is fine by me.
Tatu Ulmanen