Эффективный, краткий способ найти следующего подходящего брата? - PullRequest
74 голосов
/ 08 февраля 2011

Придерживаясь официального 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");

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

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

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

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

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

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

Ответы [ 3 ]

92 голосов
/ 08 февраля 2011

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

9 голосов
/ 08 февраля 2011

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

jQuery.fn.nextMatching = function(selector) {
    return this.nextAll(selector).first();
}
1 голос
/ 10 ноября 2014

Редактировать, Обновлено

Использование Следующий селектор братьев и сестер («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(). Piece пытается разобрать строковый аргумент селектора, имеющий несколько selector, разделенных запятыми. Возвращает .first() элемент одиночных или разделенных запятыми селекторов, предоставленных в качестве аргумента, или элемент this, если для .nextMatchTest() не указан аргумент selector. Похоже, что возвращать те же результаты на 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
};

$.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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...