views:

226

answers:

6

Given this sample markup (assuming a random number of elements between .outer and .inner:

<div class="outer">
    <div>
        <div>
            <div class="inner"></div>
        </div>
    </div>
</div>

I can set up jQuery to select the outer and inner divs as such:

$outer = $('.outer');
$inner = $outer.find('.inner')

Works fine.

However, let's say I want to allow an unlimited nesting of this logic, so I may have this:

<div class="outer"> div a
    <div class="inner"> div b
        <div class="outer"> div c
            <div class="inner"> div d </div>
        </div>
    </div>
</div>

In that situation, when selecting div a via .outer I want to match it with only div b. In otherwords, I want to exclude ancestors of a nested .outer ancestor.

I'd like to have parings of outer and inner(s) contained within their nesting level.

I'm hoping .filter() could pull it off, but can't think of a selector that would work universally for unlimited nested patterns. Is it doable using a filter? Or maybe even a direct selector pattern?

UPDATE:

I think something like this could work, but not sure how one can (or if it's allowed) reference 'this' within a selector:

$outer = $('.outer');
$inner = $outer.not('this .outer').find('.inner')

UPDATE 2:

I should have mentioned this intially: .inner will always be a descendant of .outer but not necessarily an immediate child.

UPDATE 3:

Here's some test samples of HTML that could be used. In each case, I'd want to be able to select the .outer and pair up the .inner's it contains between itself and the nested outer. For clarity, I added names to each div (outer-x pairs with inner-x)

//sample 1
<div class="outer"> outer-a
    <div>
        <div class="inner"> inner-a
            <div class="outer"> inner-b
                <div class="inner"> inner-b </div>
            </div>
        </div>
    </div>
    <div>
        <div class="inner"> inner-a </div>
    </div>
</div>

//sample 2
<div class="outer"> outer-a
        <div class="inner"> inner-a
            <div class="outer"> inner-b
                <div>
                    <div class="inner"> inner-b </div>
                </div>
            </div>
        </div>
</div>

//sample 3
<div class="outer"> outer-a
        <div class="inner"> inner-a
            <div class="outer"> inner-b
                <div class="inner"> inner-b
                    <div class="outer"> outer-c 
                        <div class="inner"> inner-c</div>
                    </div>
                </div>
            </div>
        </div>
</div>

//bonus sample (we're trying to avoid this)
<div class="outer"> outer-a
        <div class="inner outer"> inner-a outer-b
            <div class="inner"> inner-b </div>
        </div>
</div>

UPDATE 4

I think I ended up going down a similar path as gnarf. I ended up with this:

var $outer = $('.outer');
var $inner = $outer.find('.inner').filter(function(){
    $(this).each(function(){
        return $(this).closest('.outer') == $outer; 
  });                                                                  
});

Am I on the right track there? It's not working so I assume I have a bit of a logic error still.

+1  A: 
$('.outer').children('.inner');

That would select any inner that sits immediately below an outer. I'm not really clear on which div you're trying to select for. Is it a, b, c, or d? Having something called outer nested within something called inner doesn't make a whole lot of sense. Maybe if you could provide a more specific example?

jasongetsdown
+1 - This is probably the best bet assuming that the `.inner` is always a direct child of the `.outer` and not nested another level down...
gnarf
sorry for not clarifying that. That is, indeed, a variable. Inner will be a descendant, but not necessarily an immediate child.
DA
A: 

You could use some css magic:

$('.outer>.inner')

Shold give you only first level of .inner elements. :)

Ionut Staicu
That selector will match both `.inner`'s
gnarf
as gnarf points out, that's the catch. Using CSS selectors doesn't seem to be an option.
DA
+2  A: 

If the .inner are always direct children of .outers - children() is probably your best bet (jasongetsdown's answer)

If you need something that looks even deeper, you could do something like this:

var $outer = $('.outer').first(); // grab the first .outer
$outer.find('.inner').filter(function() {
    // only if the closest parent .outer is the same as the .outer we are looking in
    return $(this).closest('.outer').get(0) == $outer.get(0);
}).css('border','1px solid #000');

jsfiddle demo

gnarf
@gnarf...I think that's close to what I was thinking too. I added an update to my post that uses a filter function as well. Mine doesn't work so will try your syntax next!
DA
By the way, +1 an hour ago, nice one. I'm pretty sure you are correct, this seems like the right logic.
Kobi
+1  A: 

If assume correctly you want to select all .inner that is beneath an .outer, except if there is an .outer inbetween, perhaps following will work (untested):

$('.outer:not(.inner):has(:not(.outer) .inner:not(.outer), > .inner:not(.outer))');

updated after test on OP example HTML at http://jsfiddle.net/cEwBT/1/

azatoth
Your assumption is correct. However, that sample won't work. I first cache 'outer' and then want to find all inners that aren't a child of a nested outer. The catch is that my cached outer *is* a .outer so would be excluding itself.
DA
I've updated it now after some test over at jsfiddle
azatoth
+5  A: 

Here's another option. Suppose you have the .outer o, this will select all inners under it:

o.find('.inner').not(o.find('.outer .inner'))

It should work identically to gnarf's answer, but a bit simpler.

First, it finds all inners under this outer.
Next, remove all inners that are descendants of other outers

Interactive working example: http://jsfiddle.net/Zb9gF/

Selector performance seems to be much better using this method as opposed to the .filter() as well: http://jsperf.com/selector-test-find-not

Kobi
Brilliant! As usual, the answer is simpler than the path I end up heading down. I was struggling with the syntax needed to combined a cached object with a css selector and the find() logic didn't occure to me. Thanks!
DA
Thanks! By the way, the question is very interesting and challenging, it took a lot of work to get to this simple line...
Kobi
Nice optimization, wish I'd thought of it first :) -- Was just playin with the usefulness of [`.not()`](http://api.jquery.com/not) yesterday for [this answer](http://stackoverflow.com/questions/3089045).
gnarf
why do you need to find outers at first? why not to look just for inners and then get closest outers? see my comment also
vittore
Hey, this question qualifies me for the silver `jQuery` badge!
Kobi
it just finds your answer to be awesome than =)
vittore
@Kobi - Grats - I'm sitting pretty close myself - [as seen on odata](http://odata.stackexchange.com/stackoverflow/s/89/how-many-upvotes-do-i-have-for-each-tag?UserId=91914) :)
gnarf
A: 

I wonder why not to select .inner at first, and then get closest .outer ?

$inner = $('.inner');
$outer = $inner.closest('.outer');

in case some inner blocks could be not within outer blocks at all, use this first line instead

$inner = $('.outer .inner');

vittore
The question here is in the other direction. Given *one* `outer`, how do you find its `inner` divs?
Kobi
see. my question is why not to go other direction. having .outer given, Kobi's answer works perfect. but if you just need to get all inners for all outers - may be you could start from scratch so to say
vittore