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


придерживаясь официального API jQuery, есть ли более сжатый, но не менее эффективный способ найти следующий брат элемента, который соответствует данному селектору, кроме использования nextAll С :first псевдо-класс?

когда я говорю официальный API, я имею в виду не взламывать внутренние устройства, идя прямо шипеть, добавляя плагин в микс и т. д. (Если мне придется это сделать, пусть будет так, но это не то, что этот вопрос.)

например, учитывая это структура:

<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.фу", я могу это сделать:

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 может передать все это шипеть, и шипение было тщательно оптимизировано. Наивный цикл выше создает и выбрасывает всевозможные временные объекты и должен каждый раз повторно анализировать селектор, не удивительно, что он медленный.

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

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

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

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

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

3 72

3 ответа:

вы можете пройти множественный селектор до .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.

Как насчет использования first способ:

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

Редактировать, Обновляется!--13-->

использование селектор следующих братьев и сестер ("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>

Примечание, еще не добавлено или не опробовано в тесте сравнения. Не уверен, если на самом деле более эффективным, чем .nextAll() реализация. Часть пытается разобрать строковый аргумент селектора, имеющий несколько разделенных запятыми selector ' s . Возвращает .first() элемент одиночных или разделенных запятыми селекторов, предоставленных в качестве аргумента, или this элемент, если нет