Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 -->
                <child></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.

like image 578
Dan Lugg Avatar asked Oct 10 '22 23:10

Dan Lugg


1 Answers

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
like image 165
CAFxX Avatar answered Oct 31 '22 10:10

CAFxX