4

Based on the different answers received, the only solution using jQuery without implementing our own selector function is a basic selector with ":first" or followed by ".eq(0)" for jQuery.

I wanted to know if their were any kind of max depth option or a function like "get only the first result then return it" but it seems it's not available in jQuery since even :first in the selector does not seem to be faster that a call to .eq(0) Thanks for all the comments.


Original post:

I'm wondering if a function like "closest" is available for the children elements.

Let's say I have an html structure like that:

div.container > [* some elements, I can't use children()] > div.sub1 > div.sub2 > div.sub1 > [other various children elements]

If I want the first div.sub1 I'm doing something like:

$("div.container").find("div.sub1").eq(0)

The problem is that we cannot specify a "max depth" so it will be searched in ALL the children. I see it as a performance issue.

I'm talking about "max depth" because I could list the possibles selectors cases but it would be a long list, about 6-10 items depending on the case.

Anyone has a solution for this?

Thank you

Micaël Félix
  • 2,697
  • 5
  • 34
  • 46
  • Why can't you use `children()`? – Matt Jan 25 '12 at 18:26
  • 1
    @Matt `.children()` only checks 1 level deep (e.g. `.container > .child')`. – PeeHaa Jan 25 '12 at 18:27
  • My reference is not the direct parent of the element I'm searching. So children will not find anything. – Micaël Félix Jan 25 '12 at 18:27
  • 3
    Three days ago, the same question was asked: [similar to jquery .closest() but traversing descedents?](http://stackoverflow.com/q/8961770/938089?similar-to-jquery-closest-but-traversing-descedents/8962023#8962023). Duplicate? – Rob W Jan 25 '12 at 18:31
  • 1
    @RobW: Not a dupe, as the OP wants to be able to limit the depth – qwertymk Jan 25 '12 at 18:37
  • @qwertymk If I interpreted the question correctly, the OP wants a depth, so that the jQuery method does not select all occurrences. By stopping on the first match, this requirement is met. If a depth has to be implemented, only a counter should be added to the linked "possible dupe". – Rob W Jan 25 '12 at 18:40
  • The problem here is that even if jQuery let you specify a max depth it would *still* have to select everything that matches first before it can determine if it's over the max depth. This is just the breaks of the game when using CSS-style selectors. That said, it's really kind of pointless anyways. 6-10 extra items being initially matched doesn't amount to any real performance penalty. – Chris Pratt Jan 25 '12 at 18:51
  • @ChrisPratt If the depth selector is correctly programmed it would skip children that is being "too deep", so depending on the depth of the html structure the time spent could be significantly decrased. – Micaël Félix Jan 25 '12 at 19:05
  • 1
    If you are looking for max performance, then you will want to write your own selector function (not using jQuery) that traverses the hierarchy and stops when it finds the first match. If you are looking for best practice with jQuery, just use `:first` or `.eq(0)` and be on to the next piece of code to write. Your question is unclear what you really want so we cannot help further without understanding the real goals. – jfriend00 Jan 25 '12 at 19:05
  • I am not sure, if you are looking for a search function working level by level (a breadth first search). If so, you can yous this plugin: http://plugins.jquery.com/closestDescendant/ – TLindig Apr 08 '13 at 23:17

1 Answers1

7

You can possibly get this by combining the child-selector (>) and the wildcard (*) at various amounts:

// max-depth: grand-children
$('.container').find('> .sub1, > * > .sub1').first();

// max-depth: great-grand-children
$('.container').find('> .sub1, > * > .sub1, > * > * > .sub1').first();

And, since this can be rather tedious to have hard-coded, you can use a custom method to build the selector for you:

jQuery.fn.findAtDepth = function (selector, maxDepth) {
    var depths = [], i;

    if (maxDepth > 0) {
        for (i = 1; i <= maxDepth; i++) {
            depths.push('> ' + new Array(i).join('* > ') + selector);
        }

        selector = depths.join(', ');
    }

    return this.find(selector);
};

$('.container').findAtDepth('.sub1', 3).first();

Examle: http://jsfiddle.net/V82f3/2/


The primary pitfall of this is that it's limited to relatively simple selectors (or possibly just to single selectors):

$('.container').findAtDepth('.sub1, .sub2', 3).first();

This will search for .sub1 at a max depth of 3, but for .sub2 at any depth.

Jonathan Lonowski
  • 121,453
  • 34
  • 200
  • 199