高效,简洁的方法找到下一个匹配的兄弟姐妹?

坚持使用官方的 jQuery API,除了使用 nextAll:first伪类之外,还有更简洁但效率更高的方法来查找匹配给定选择器的元素的下一个同级元素吗?

当我说到官方 API 时,我的意思是不要黑进内部,直接进入 Sizzle,添加一个插件,等等(如果我最终不得不这样做,那就这样做,但这不是这个问题

例如,考虑到这种结构:

<div>One</div>
<div class='foo'>Two</div>
<div>Three</div>
<div class='foo'>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div class='foo'>Eight</div>

如果我在 this中有一个 div(可能在 click处理程序中) ,并且希望找到与选择器“ div.foo”匹配的下一个兄弟 div,我可以这样做:

var nextFoo = $(this).nextAll("div.foo:first");

... 而且它很有效(例如,如果我以“ Five”开头,它会跳过“ Six”和“ Seven”,为我找到“ Eight”) ,但它很笨重,如果我想匹配几个选择器中的任何一个,它就会笨重得多。(当然,它比原始 DOM 循环更简洁... ...)

我基本上想要:

var nextFoo = $(this).nextMatching("div.foo");

nextMatching可以接受各种选择器。我一直很惊讶 next(selector)没有这样做,但它没有,而且医生对它的作用很清楚,所以..。

我总是可以编写并添加它,尽管如果我这样做并坚持使用已发布的 API,效率会变得非常低。例如,一个 na & iuml; ve next循环:

jQuery.fn.nextMatching = function(selector) {
var match;


match = this.next();
while (match.length > 0 && !match.is(selector)) {
match = match.next();
}
return match;
};

慢一点而不是 nextAll("selector:first")。这并不奇怪,nextAll可以把整个事情交给 Sizzle,而 Sizzle 已经完全优化了。上面的循环创建并抛弃了所有类型的临时对象,并且每次都必须重新解析选择器,因此它的速度很慢也就不足为奇了。

当然,我不能只是在最后扔一个 :first:

jQuery.fn.nextMatching = function(selector) {
return this.nextAll(selector + ":first"); // <== WRONG
};

... 因为虽然它可以用于简单的选择器,比如“ div.foo”,但是如果使用“ any of few”选项,比如“ div.foo,div.bar”,它就会失败。

编辑 : 对不起,应该说: 最后,我可以只使用 .nextAll(),然后在结果上使用 .first(),但是那样的话,jQuery 将不得不访问所有的兄弟节点,只是为了找到第一个。我希望它在得到匹配结果时停止,而不是浏览完整的列表,这样它就可以抛弃除第一个结果之外的所有结果。(尽管它似乎发生得很快; 请参阅前面链接的 速度比较中的最后一个测试用例。)

先谢谢你。

24805 次浏览

使用 first方法如何:

jQuery.fn.nextMatching = function(selector) {
return this.nextAll(selector).first();
}

你可以将一个 多重选择器多重选择器传递给 .nextAll(),然后在结果上使用 .first(),如下所示:

var nextFoo = $(this).nextAll("div.foo, div.something, div.else").first();

编辑: 只是为了比较,在这里它被添加到测试套件: http://jsperf.com/jquery-next-loop-vs-nextall-first/2这种方法是如此之快,因为它是一个简单的组合,将 .nextAll()选择器移交给本机代码(当前的每个浏览器) ,只是采取结果集的第一个... ... 方式比任何纯粹使用 JavaScript 的循环都要快。

编辑,更新

利用 下一个兄弟姐妹选择器(“ prev ~ brothers”)

jQuery.fn.nextMatching = function nextMatchTest(selector) {
return $("~ " + selector, this).first()
};

Http://jsperf.com/jquery-next-loop-vs-nextall-first/10

jQuery.fn.nextMatching = function nextMatchTest(selector) {
return $("~ " + selector, this).first()
};
var nextFoo = $("div:first").nextMatchTest("div.foo");
console.log(nextFoo)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>One</div>
<div class='foo'>Two</div>
<div>Three</div>
<div class='foo'>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div class='goo'>Eight</div>


注意,尚未添加到比较测试或在比较测试中尝试。不确定是否实际上比 .nextAll()实现更有效。片段尝试解析具有多个逗号分隔的 selector的选择器字符串参数。返回作为参数提供的单个或逗号分隔的选择符的 .first()元素,如果没有向 .nextMatchTest()提供 selector参数,返回 this元素。似乎在 chrome 37,ie11返回相同的结果

V2

$.fn.nextMatching = function (selector) {
var elem = /,/.test(selector) ? selector.split(",") : selector
, sel = this.selector
, ret = $.isArray(elem) ? elem.map(function (el) {
return $(sel + " ~ " + $(el).selector).first()[0]
}) : $(sel + " ~ " + elem).first();
return selector ? $(ret) : this
};

$.fn.nextMatching = function (selector) {
var elem = /,/.test(selector) ? selector.split(",") : selector
, sel = this.selector
, ret = $.isArray(elem) ? elem.map(function (el) {
return $(sel + " ~ " + $(el).selector).first()[0]
}) : $(sel + " ~ " + elem).first();
return selector ? $(ret) : this
};


var div = $("div:first")
, foo = div.nextMatching()
, nextFoo = div.nextMatching("div.foo")
, nextFooMultiple = div.nextMatching("div.foo, div.goo");
nextFooMultiple.css("color", "green");
nextFoo.css("color", "blue");
console.log(foo);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div>One</div>
<div class='foo'>Two</div>
<div>Three</div>
<div class='foo'>Four</div>
<div>Five</div>
<div>Six</div>
<div>Seven</div>
<div class='goo'>Eight</div>