Is there a function similar to jQuery
.closest()
but for traversing descendants and returning only closest ones?
I know that there is
-
I think that first you have to define what "closest" means. If you mean the descendant node matching your criteria that is the shortest distance away in terms of parental links, then using ":first" or ".eq(0)" won't necessarily work:
<div id='start'>
<div>
<div>
<span class='target'></span>
</div>
</div>
<div>
<span class='target'></span>
</div>
</div>
In that example, the second ".target" <span>
element is "closer" to the "start" <div>
, because it's only one parental hop away. If that's what you mean by "closest", you'd need to find the minimum distance in a filter function. The result list from a jQuery selector is always in DOM order.
Maybe:
$.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;
};
That's sort-of a hack plugin because of the way it builds the result object, but the idea is that you've got to find the shortest parental path out of all the matching elements you find. This code biases towards elements leftward in the DOM tree because of the <
check; <=
would bias rightwards.
讨论(0)
-
Rob W's answer didn't quite work for me. I adapted it to this which did work.
//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)
-
Even though it's an old topic I could not resist implementing my closestChild.
Delivers the first found descendant with least traveling ( breath first ).
One is recursive (personal favorite ), other using a todo list so without recursion as jQquery extensions.
Hope someone benefits.
Note : The recursive gets stack overflow, and I improved the other, now simular to previous answer given.
jQuery.fn.extend( {
closestChild_err : function( selector ) { // recursive, stack overflow 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)
-
According to your definition of closest
, I've written 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);
讨论(0)
-
I cooked up this, no implementation for positional selectors (they need more than just matchesSelector
) yet:
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 is probably some bugs though, didn't test it very much.
讨论(0)
-
There is an excellent article that points out that what the OP requires can be achieved easily with closest [Geeks for Geeks]
1https://www.geeksforgeeks.org/jquery-closest-with-examples/
讨论(0)
- 热议问题