Эффективный, лаконичный способ найти следующий соответствующий друг?

Придерживаясь официального 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, соответствующий селектору div.foo, я могу сделать это:

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

... и он работает (если я начинаю с "Пятерки", например, он пропускает "Шесть" и "Семь" и называет "Восьмерка" для меня), но он неуклюж, и если я хочу сопоставить первый любого из нескольких селекторов, он становится намного clunkier. (Конечно, это лот более кратким, чем сырой цикл 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 полностью оптимизирован. Верхняя петля na & iuml; ve выше создает и выбрасывает всевозможные временные объекты и должна каждый раз перебирать селектор, и не удивительно, что это замедляется.

И, конечно, я не могу просто выбросить :first в конец:

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

... потому что, хотя это будет работать с простыми селекторами, такими как "div.foo", он будет терпеть неудачу с опцией "любой из нескольких", о которой я говорил, например, "div.foo, div.bar".

Изменить: Извините, должен был сказать: Наконец, я мог бы просто использовать .nextAll(), а затем использовать .first() для результата, но тогда jQuery придется посетить всех братьев и сестер только для найти первый. Я бы хотел, чтобы он остановился, когда он получил матч, а не прошел полный список, чтобы он мог отбросить все результаты, кроме первого. (Хотя это происходит очень быстро, см. Последний тестовый пример в сравнение скорости, связанный ранее.)

Спасибо заранее.

Ответ 1

Вы можете передать множественный селектор в .nextAll() и использовать .first(), выполните следующие действия:

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

Edit: просто для сравнения, здесь он добавлен в тестовый набор: http://jsperf.com/jquery-next-loop-vs-nextall-first/2 Этот подход намного быстрее, потому что это простая комбинация передачи .nextAll() селектор отключится до нативного кода, когда это возможно (каждый текущий браузер), и просто беря первый из набора результатов.... быстрее, чем любой цикл, который вы можете делать только в JavaScript.

Ответ 2

Как использовать метод first:

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

Ответ 3

Изменить, обновить

Использование 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

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>