开发者

Finding all "X-generation" descendants using jQuery

Solved (sort of)

Well, I think I solved it (barring edge cases) using the following:

function findByDepth(parent, child, depth){
    var children = $();
    $(child, $(parent)).each(function(){
        if($(this).parentsUntil(parent, child).length == (depth - 1)){
            children = $(children).add($(this));
        }
    });
    return children;
}

// this would successfully return the 3.X elements of an HTML snippet structured
// as my XML example, where <parent> = #parent, etc.
var threeDeep = findByDepth('#parent', '.child', 3);

However somebody has to get the accepted answer here, and I'm not going to answer it myself and abscond with your well earned rep. So, if anyone wants to add anything, such as provide insight into optimizing this function (before I go and $.extend() it in) I'll likely mark your answer correct, otherwise falling back to marking whoever was first on my initial question.

By the way, check it in the fiddle: http://jsfiddle.net/5PDaA/

Sub-update

Looking again at @CAFxX's answer, I realized that his approach is probably faster, taking advantage of querySelectorAll in browsers that can. Anyways, I revised his approach to the following, yet it's still giving me guff:

$.fn.extend({
    'findAtDepth': function(selector, depth){
        var depth = parseInt(depth) || 1;
        var query = selector;
        for(var i = 1; i < depth; i++){
            query += (' ' + selector);
        }
        return $(query, $(this)).not(query + ' ' + selector);
    }
});

It works fine the first time, however as context changes to an element found in the selector, it fails for some reason.


Update

Alright, I was foolishly unclear, and uninformed as to the specs for what I'm doing. Since I've reviewed my implementation I'll update here; I'll mark the earliest answer that meets my initial requirements given people think me a fool for updating so gratuitously (I wouldn't blame you) however my bonus mention below is essentially a requirement. I'd post another question, but it'd likely get closed from duplication. Regardless, +1's all around for your patience:

Depth specification of a given child is necessary (given it's wrapped in a function or otherwise) thus isolating a child and equally nested (not necessarily siblings) matching elements.

For instance (XML for brevity):

<!-- depth . sibling-index-with-respect-to-depth -->

<parent>
    <child>                     <!-- 1.1 -->
        <child>                 <!-- 2.1 -->
            <child>             <!-- 3.1 -->
                <ch开发者_开发百科ild></child> <!-- 4.1 -->
            </child>            
            <child>             <!-- 3.2 -->
                <child></child> <!-- 4.2 -->
            </child>            
        </child>                
        <child>                 <!-- 2.2 -->
            <child></child>     <!-- 3.3 -->
        </child>                
    </child>                    
</parent>

Given a depth specified of 2, all 2.X elements are selected. Given 4 all 4.X, and so on.


Original Question

Using the native functionality of jQuery, is there a way to select only the "first-generation" of descendants matching a selector? For instance:

Note: The following is only an example. .child elements are nested in a parent at an arbitrary level.

Bonus: As my proposed syntax below indicates, an answer that provides a way to specify a depth to which the selection should traverse would be incredible.

// HTML
<div id="parent">
    <div>
        <div class="child"> <!-- match -->
            <div>
                <div class="child"> <!-- NO match -->
                </div>
            </div>
        </div>
        <div class="child"> <!-- match -->
            <div>
                <div class="child"> <!-- NO match -->
                </div>
            </div>
        </div>
    </div>
</div>

And:

// jQuery
$('#parent').find('.child:generation(1)'); // something in that vein

Trying to select from the context of #parent, the jQuery :first doesn't work here as it only hits the first matched .child.


Try this (KISS!):

$("#parent .child").not(".child .child");

edit: to get the second level:

$("#parent .child .child").not(".child .child .child");

the third:

$("#parent .child .child .child").not(".child .child .child .child");

and so on... so you could have (untested):

function findChildrenDeepDown(parentSelector, level) {
  level = parseInt(level);
  var firstSelctor = parent, notSelector = ".child", i;
  for (i=0; i<level; i++) {
    firstSelector += " .child";
    notSelector += " .child";
  }
  return $(firstSelector).not(notSelector);
}

findChildrenDeepDown("#parent", 3); // find third-level children of #parent


In general, I think you'd have to find all of the .child elements and then throw away those that have .child ancestors:

$('#parent .child').filter(function() {
    return $(this).parent().closest('.child').length == 0;
});

This doesn't handle things like this though:

<div id="parent">
    <div class="child"> <!-- match -->
        <div>
            <div class="child"> <!-- NO match -->
            </div>
        </div>
    </div>
    <div>
        <div>
            <div class="child"> <!-- NO match wanted but it will match -->
            </div>
        </div>
    </div>
</div>

but maybe it will be sufficient for the structure of your HTML.


you can get it like this :

$("#parent").find(">div>.child")

EDIT

If first child can be in a random depth, you can use

$("#parent").find(".child").siblings();
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜