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


77

坚持使用官方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>

如果我有一个divin this(也许在click处理程序中,无论如何)并且想要找到与选择器“ div.foo”相匹配的下一个同级div,我可以这样做:

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

...并且可以正常工作(例如,如果我以“五个”开头,则跳过“六个”和“七个”并为我找到“八个”),但是它很笨拙,并且如果我要匹配任何一个中的第一个几个选择器,就会变得笨拙。(当然,这是一个很多更简洁比原始DOM循环将是...)

我基本上想要:

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

...nextMatching可以接受所有选择器 我总是很惊讶next(selector)没有这样做,但是没有这么做,并且文档清楚其功能,所以...

我总是可以编写并添加它,尽管如果这样做并坚持使用已发布的API,事情将会变得非常低效。例如,一个朴素的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”之类的简单选择器,但由于我提到的“多个选项”选项会失败,例如“ div.foo,div.bar”。

编辑:对不起,应该说:最后,我可以使用.nextAll()然后.first()对结果使用,但是jQuery将不得不访问所有同级对象以找到第一个。我希望它在获得比赛结果时停止,而不是浏览完整列表,这样它就可以丢弃除第一个以外的所有结果。(尽管它似乎发生得非常快;请参阅前面链接的速度比较中的最后一个测试用例。)

提前致谢。


您有没有找到比这更快的方法.nextAll().first()
Ennui 2013年

1
@Ennui:不,那似乎是最好的方法。
TJ Crowder 2014年

Answers:


95

您可以将多重选择器传递给.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纯粹做得更快。


1
抱歉,我应该在我的问题中提到这一点(现在有):我不想不必要地拜访以下所有兄弟姐妹,而只是找到第一个兄弟姐妹。
TJ Crowder

@TJ-然后,答案是否定的,没有更好的内置方法来做到这一点。我请求.next(selector, true)或类似的过载你在说什么,遍历直到它的发现,而不是返回,如果它的发现,但它看起来像这种讨论是不是对.next()文档了:api.jquery.com/next
尼克Craver

@TJ-我将测试用例添加到套件中,由于添加到答案中的原因,您会看到它在每个现代浏览器中都更快:)
Nick Craver

2
哪个更快的'.first()'或'.eq(0)'?
Mark Schultheiss

5
@马克-.eq(0)将是绝对的最快的,因为什么是什么.first()要求,但相较于一切的区别有别的是微不足道的,所以我会去在这种情况下,更可读的代码。话虽如此,我的示例不是插件...如果人们不直接使用它,则一定要使用.eq(0)
尼克·克拉弗

9

如何使用该first方法:

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

抱歉,我应该在我的问题中提到这一点(现在有):我不想不必要地拜访以下所有兄弟姐妹,而只是找到第一个兄弟姐妹。
TJ Crowder

1

编辑,更新

使用下一个兄弟姐妹选择器(“上一个〜兄弟姐妹”)

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

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


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

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


+1如果可以在IE上运行,那就太好了。但是可悲的是,它没有,IE无法找到该元素(我想jQuery用于无根~组合器的任何解决方法都不适用于IE)。(顺便说一句,$(selector, context)可能会在某个时候消失,并且$(context).find(selector)无论如何都将转换为它,因此直接使用它会稍微快一些。)jsperf.com/jquery-next-loop-vs-nextall-first/11但可悲的是,如果甚至现代的IE也无法处理...
TJ Crowder 2014年

@ guest:到那时,我们不得不担心它是否在所有情况下都有效,无论如何selector都不可靠,在1.7中已弃用,而在1.9中已删除(即使它仍然存在,它是内部的一部分,仅在那里支持live迁移插件)。
TJ Crowder 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.