Like jQuery.closest (), but intersecting descendants?

Is there a function similar to jQuery .closest() , but for moving descendants and returning only the closest ones?

I know that there is a .find() function, but it returns all possible matches, not the closest ones.

Edit:

Here is a definition of the closest (at least for me):

First, all the children intersect, then all the children of each child pass.

In the example below, id='2' is the closest .closest descendants of id="find-my-closest-descendant"

 <div id="find-my-closest-descendant"> <div> <div class="closest" Id='1'></div> </div> <div class="closest" Id='2'></div> </div> 

See the JSfiddle link .

+111
jquery
Jan 22 2018-12-12T00:
source share
15 answers

According to your definition of closest , I wrote the following plugin:

 (function($) { $.fn.closest_descendent = function(filter) { var $found = $(), $currentSet = this; // Current place while ($currentSet.length) { $found = $currentSet.filter(filter); if ($found.length) break; // At least one match: break loop // Get all children of the current set $currentSet = $currentSet.children(); } return $found.first(); // Return first match of the collection } })(jQuery); 
+42
Jan 22 2018-12-22T00:
source share

If by "closest" descendant you mean the first child, then you can do:

 $('#foo').find(':first'); 

Or:

 $('#foo').children().first(); 

Or, to find the first occurrence of a specific element, you can do:

 $('#foo').find('.whatever').first(); 

Or:

 $('#foo').find('.whatever:first'); 

Indeed, we need a firm definition of what the "closest descendant" means.

eg.

 <div id="foo"> <p> <span></span> </p> <span></span> </div> 

What <span> will $('#foo').closestDescendent('span') returned?

+110
Jan 22 2018-12-22T00:
source share

You can use find with the :first selector:

 $('#parent').find('p:first'); 

In the above line, the first <p> element will be found in the descendants of #parent .

+17
Jan 22 2018-12-22T00:
source share

How about this approach?

 $('find-my-closest-descendant').find('> div'); 

This "direct children" selector works for me.

+2
Nov 15 '17 at 11:18
source share

Pure JS solution (using ES6).

 export function closestDescendant(root, selector) { const elements = [root]; let e; do { e = elements.shift(); } while (!e.matches(selector) && elements.push(...e.children)); return e.matches(selector) ? e : null; } 



Example

Given the following structure:

 div == $ 0
 β”œβ”€β”€ div == $ 1
 β”‚ β”œβ”€β”€ div
 β”‚ β”œβ”€β”€ div.findme == $ 4
 β”‚ β”œβ”€β”€ div
 β”‚ └── div
 β”œβ”€β”€ div.findme == $ 2
 β”‚ β”œβ”€β”€ div
 β”‚ └── div
 └── div == $ 3
     β”œβ”€β”€ div
     β”œβ”€β”€ div
     └── div
 closestDescendant($0, '.findme') === $2; closestDescendant($1, '.findme') === $4; closestDescendant($2, '.findme') === $2; closestDescendant($3, '.findme') === null; 

 function closestDescendant(root, selector) { const elements = [root]; let e; do { e = elements.shift(); } while (!e.matches(selector) && elements.push(...e.children)); return e.matches(selector) ? e : null; } const [$0, $1, $2, $3, $4] = [0, 1, 2, 3, 4].map(x => document.querySelector('#e${x}')); console.log(closestDescendant($0, '.findme')); // $2 console.log(closestDescendant($1, '.findme')); // $4 console.log(closestDescendant($2, '.findme')); // $2 console.log(closestDescendant($3, '.findme')); // null 
 <div id="e0"> <div id="e1"> <div></div> <div id="e4" class="findme"></div> <div></div> <div></div> </div> <div id="e2" class="findme"> <div></div> <div></div> </div> <div id="e3"> <div></div> <div></div> <div></div> </div> </div> 

+1
Oct 02 '18 at 13:56
source share

If someone is looking for a clean JS solution (using ES6 instead of jQuery), here is the one I'm using:

 Element.prototype.QuerySelector_BreadthFirst = function(selector) { let currentLayerElements = [...this.childNodes]; while (currentLayerElements.length) { let firstMatchInLayer = currentLayerElements.find(a=>a.matches && a.matches(selector)); if (firstMatchInLayer) return firstMatchInLayer; currentLayerElements = currentLayerElements.reduce((acc, item)=>acc.concat([...item.childNodes]), []); } return null; }; 
+1
Mar 13 '19 at 14:41
source share

I think you must first determine what "closest" means. If you mean a descendant of a node that matches your criteria, which is the shortest distance in terms of parent links, then using ": first" or ".eq (0)" will not necessarily work:

 <div id='start'> <div> <div> <span class='target'></span> </div> </div> <div> <span class='target'></span> </div> </div> 

In this example, the second element .target <span> is closer to start <div> because it has only one parent hop away. If this is what you mean by β€œclosest,” you need to find the minimum distance in the filter function. The list of results from the jQuery selector is always in DOM order.

May be:

 $.fn.closestDescendant = function(sel) { var rv = $(); this.each(function() { var base = this, $base = $(base), $found = $base.find(sel); var dist = null, closest = null; $found.each(function() { var $parents = $(this).parents(); for (var i = 0; i < $parents.length; ++i) if ($parents.get(i) === base) break; if (dist === null || i < dist) { dist = i; closest = this; } }); rv.add(closest); }); return rv; }; 

This type of hacked plugin is due to the way it creates the result object, but the idea is that you need to find the shortest parent path of all the matching elements that you find. This code shifts to elements to the left in the DOM tree due to the check < ; <= will shift to the right.

0
Jan 22 '12 at 14:50
source share

I prepared this, not for positional selectors (they need more than just matchesSelector ):

Demo: http://jsfiddle.net/TL4Bq/3/

 (function ($) { var matchesSelector = jQuery.find.matchesSelector; $.fn.closestDescendant = function (selector) { var queue, open, cur, ret = []; this.each(function () { queue = [this]; open = []; while (queue.length) { cur = queue.shift(); if (!cur || cur.nodeType !== 1) { continue; } if (matchesSelector(cur, selector)) { ret.push(cur); return; } open.unshift.apply(open, $(cur).children().toArray()); if (!queue.length) { queue.unshift.apply(queue, open); open = []; } } }); ret = ret.length > 1 ? jQuery.unique(ret) : ret; return this.pushStack(ret, "closestDescendant", selector); }; })(jQuery); 

There are probably some errors; they have not tested very much.

0
Jan 22 2018-12-22T00:
source share

Rob W's answer didn’t quite work for me. I adapted it to what really worked.

 //closest_descendent plugin $.fn.closest_descendent = function(filter) { var found = []; //go through every matched element that is a child of the target element $(this).find(filter).each(function(){ //when a match is found, add it to the list found.push($(this)); }); return found[0]; // Return first match in the list } 
0
Jul 15 '14 at 7:10
source share

I would use the following to include the object itself if it matches the selector:

  var jTarget = $("#doo"); var sel = '.pou'; var jDom = jTarget.find(sel).addBack(sel).first(); 

Markup:

 <div id="doo" class="pou"> poo <div class="foo">foo</div> <div class="pou">pooo</div> </div> 
0
Sep 18 '14 at 7:36
source share

Despite the fact that this is an old topic, I could not resist the implementation of my closest shell. Delivers the first descendant to be found with the least amount of travel (respiration first). One of them is recursive (a personal favorite), the other is a list of volumes without recursion as jQquery extensions.

Hope someone wins.

Note. A recursive stack overflow, and I improved on another, now simulative to the previous answer.

 jQuery.fn.extend( { closestChild_err : function( selector ) { // recursive, Qaru when not found var found = this.children( selector ).first(); if ( found.length == 0 ) { found = this.children().closestChild( selector ).first(); // check all children } return found; }, closestChild : function( selector ) { var todo = this.children(); // start whith children, excluding this while ( todo.length > 0 ) { var found = todo.filter( selector ); if ( found.length > 0 ) { // found closest: happy return found.first(); } else { todo = todo.children(); } } return $(); }, }); 
0
Apr 26 '15 at 10:33
source share

The following plugin returns the nth closest descendants.

 $.fn.getNthClosestDescendants = function(n, type) { var closestMatches = []; var children = this.children(); recursiveMatch(children); function recursiveMatch(children) { var matches = children.filter(type); if ( matches.length && closestMatches.length < n ) { var neededMatches = n - closestMatches.length; var matchesToAdd = matches.slice(0, neededMatches); matchesToAdd.each(function() { closestMatches.push(this); }); } if (closestMatches.length < n) { var newChildren = children.children(); recursiveMatch(newChildren); } } return closestMatches; }; 
0
Aug 25 '15 at 16:58
source share

I was looking for a similar solution (I need all the closest descendants, i.e. width at first + all matches, no matter what level it exists), this is what I did in the end:

 var item = $('#find-my-closest-descendant'); item.find(".matching-descendant").filter(function () { var $this = $(this); return $this.parent().closest("#find-my-closest-descendant").is(item); }).each(function () { // Do what you want here }); 

Hope this helps.

0
Mar 14 '16 at 16:35
source share

You can just put

 $("#find-my-closest-descendant").siblings('.closest:first'); 
0
Aug 20 '19 at 12:26
source share

you have many options, however $("#parent").children(".child"); is a fastet. check this article for more details and test

-2
Feb 05 '15 at 21:17
source share



All Articles