类似于 jQuery.close()但是遍历子代?

是否有类似于 jQuery .closest()的函数,但是用于遍历子代并只返回最接近的子代?

我知道有 .find()函数,但它返回所有可能的匹配,而不是最接近的匹配。

编辑:

下面是最接近 (至少对我来说)的定义:

首先遍历所有的孩子,然后遍历每个孩子。

在下面给出的例子中,id='2'是最接近 .closestid="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>

请参阅 JSfiddle 链接

100180 次浏览

你可以使用 find:first选择器:

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

上面的行将在 #parent的后代中找到第一个 <p>元素。

如果你说的“最亲密的”后代指的是第一个孩子,那么你可以这样做:

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

或者:

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

或者,要查找特定元素的第一个匹配项,您可以这样做:

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

或者:

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

实际上,我们需要一个确切的定义“最近的后代”是什么意思。

例如。

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

$('#foo').closestDescendent('span')会返回哪个 <span>

我认为首先你必须定义“最接近”是什么意思。如果您指的是与您的条件相匹配的子代节点,而该子代节点与父代链接之间的距离最短,则使用“ : first”或“”。“ eq (0)”不一定有效:

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

在这个例子中,是第二个。”。Target”<span>元素“更接近”“ start”<div>,因为它只有一个父级跃点的距离。如果这就是你所说的“最近”,你需要在一个过滤器函数中找到最小距离。来自 jQuery 选择器的结果列表总是按照 DOM 顺序排列。

也许:

$.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;
};

由于它构建结果对象的方式,这有点像是一个黑客插件,但其想法是,你必须从所有匹配的元素中找到最短的父元素路径。由于 <检查,这段代码偏向于 DOM 树中向左的元素; 而 <=偏向于向右。

我编写了这个,没有位置选择器的实现(它们需要的不仅仅是 matchesSelector) :

演示: 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);

可能有一些错误,虽然,没有测试它很多。

根据您对 closest的定义,我编写了以下插件:

(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);

罗伯 · W 的回答对我来说不太管用,我把它改编成了这个,它确实管用。

//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
}

如果目标与选择器匹配,我将使用以下方法来包含目标本身:

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

标价:

<div id="doo" class="pou">
poo
<div class="foo">foo</div>
<div class="pou">pooo</div>
</div>

你有很多选择,但是 $("#parent").children(".child");是最快的。 检查这篇文章的细节和基准

尽管这是一个老话题,我还是忍不住执行我最亲近的孩子。 以最少的旅行(先呼吸)交付第一个找到的后代。 一种是递归的(个人最爱) ,另一种是使用 todo 列表,这样就不需要递归作为 jQquery 扩展。

希望有人受益。

注意: 递归得到堆栈溢出,我改进了另一个,现在类似于前面给出的答案。

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 $();
},


});

下面的插件返回第 n 个最接近的后代。

$.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;
};

我正在寻找一个类似的解决方案(我想要所有最接近的后代,即广度优先 + 所有匹配,不管它存在于哪个级别) ,以下是我最终做的:

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
});

希望这个能帮上忙。

这个方法怎么样?

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

这个“直接子女”选择器为我工作。

纯 JS 解决方案(使用 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;
}

例子

考虑到以下结构:

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>

如果有人在寻找纯 JS 解决方案(使用 ES6而不是 jQuery) ,下面是我使用的解决方案:

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;
};

你可以直接写,

$("#find-my-closest-descendant").siblings('.closest:first');

有一篇优秀的文章指出,OP 需要什么可以很容易地实现与最接近[极客的极客]
1 https://www.geeksforgeeks.org/jquery-closest-with-examples/