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

时间:2011-02-08 12:52:16

标签: jquery jquery-selectors

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

如果div中有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;
};

... markedly slowernextAll("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将不得不访问所有兄弟姐妹只是想找到第一个。我希望它在获得匹配时停止而不是通过完整列表只是因为它可以丢弃所有结果,但第一个。 (虽然它似乎发生真的快;看看之前链接的speed comparison中的最后一个测试用例。)

提前致谢。

3 个答案:

答案 0 :(得分:92)

您可以将multiple selector传递给.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 :(得分:9)

如何使用first方法:

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

答案 2 :(得分:1)

修改,更新

利用Next Siblings Selector (“prev ~ siblings”)

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

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

&#13;
&#13;
jQuery.fn.nextMatching = function nextMatchTest(selector) {
     return $("~ " + selector, this).first()
};
   var nextFoo = $("div:first").nextMatchTest("div.foo");
   console.log(nextFoo)
&#13;
<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>
&#13;
&#13;
&#13;


注意,尚未添加或尝试进行比较测试。不确定是否实际上比.nextAll()实施更有效。 Piece尝试解析具有多个逗号分隔selector的选择器字符串参数。返回作为参数提供的单个或逗号分隔选择器的.first()元素,如果没有this参数提供给selector,则返回.nextMatchTest()元素。似乎在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
};

&#13;
&#13;
$.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);
&#13;
<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>
&#13;
&#13;
&#13;