Gibt es bei der offiziellen jQuery-API eine präzisere, aber nicht weniger effiziente Möglichkeit, das nächste Geschwister eines Elements zu finden, das mit einem bestimmten Selektor übereinstimmt, das nicht nextAll
mit der :first
Pseudoklasse verwendet wird?
Wenn ich offizielle API sage, meine ich, keine Interna zu hacken, direkt zu Sizzle zu gehen, ein Plug-In in den Mix einzufügen usw. (Wenn ich das am Ende tun muss, soll es so sein, aber das ist nicht die Frage. )
ZB bei dieser Struktur:
<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>
Wenn ich ein div
In habe this
(vielleicht in einem click
Handler, was auch immer) und das nächste Geschwister-Div finden möchte, das dem Selektor "div.foo" entspricht, kann ich dies tun:
var nextFoo = $(this).nextAll("div.foo:first");
... und es funktioniert (wenn ich zum Beispiel mit "Fünf" beginne, überspringt es "Sechs" und "Sieben" und findet "Acht" für mich), aber es ist klobig und wenn ich mit dem ersten von einem übereinstimmen möchte Bei mehreren Selektoren wird es viel klobiger. (Zugegeben, es ist viel prägnanter als die rohe DOM-Schleife ...)
Ich möchte im Grunde:
var nextFoo = $(this).nextMatching("div.foo");
... wo nextMatching
kann die gesamte Auswahl an Selektoren akzeptiert werden. Ich bin immer überrascht, dass next(selector)
dies nicht funktioniert, aber nicht, und in den Dokumenten ist klar, was es tut, also ...
Ich kann es immer schreiben und hinzufügen, obwohl die Dinge ziemlich ineffizient werden, wenn ich das tue und mich an die veröffentlichte API halte. Zum Beispiel eine naivenext
Schleife:
jQuery.fn.nextMatching = function(selector) {
var match;
match = this.next();
while (match.length > 0 && !match.is(selector)) {
match = match.next();
}
return match;
};
... ist deutlich langsamer alsnextAll("selector:first")
. Und das ist nicht überraschend, nextAll
kann das Ganze an Sizzle übergeben, und Sizzle wurde gründlich optimiert. Die naive Schleife oben erzeugt und wirft alle Arten von temporären Objekten weg und muss den Selektor jedes Mal neu analysieren, keine große Überraschung, dass er langsam ist.
Und natürlich kann ich nicht einfach einen :first
am Ende werfen :
jQuery.fn.nextMatching = function(selector) {
return this.nextAll(selector + ":first"); // <== WRONG
};
... weil dies zwar mit einfachen Selektoren wie "div.foo" funktioniert, aber mit der Option "beliebig von mehreren", über die ich gesprochen habe, wie "div.foo, div.bar", fehlschlägt.
Bearbeiten : Entschuldigung, hätte sagen sollen: Schließlich könnte ich nur das Ergebnis verwenden .nextAll()
und dann verwenden .first()
, aber dann muss jQuery alle Geschwister besuchen, um das erste zu finden. Ich möchte, dass es aufhört, wenn es ein Match gibt, anstatt die vollständige Liste durchzugehen, nur damit alle Ergebnisse außer dem ersten weggeworfen werden können. (Obwohl es sehr schnell zu gehen scheint ; siehe den letzten Testfall im Geschwindigkeitsvergleich zuvor verlinkten .)
Danke im Voraus.
quelle
.nextAll().first()
?Antworten:
Sie können einen Pass mehrere Wähler auf
.nextAll()
und die Verwendung.first()
auf dem Ergebnis, wie folgt aus :var nextFoo = $(this).nextAll("div.foo, div.something, div.else").first();
Bearbeiten: Nur zum Vergleich, hier wird es der Testsuite hinzugefügt: http://jsperf.com/jquery-next-loop-vs-nextall-first/2 Dieser Ansatz ist so viel schneller, weil es eine einfache Kombination der Übergabe der
.nextAll()
Wählen Sie nach Möglichkeit den nativen Code aus (jeden aktuellen Browser) und nehmen Sie nur den ersten Teil der Ergebnismenge ... viel schneller als jede Schleife, die Sie nur in JavaScript ausführen können.quelle
.next(selector, true)
oder eine ähnliche Überladung für das, worüber Sie sprechen, angefordert und durchlaufen, bis sie gefunden wurde, anstatt zurückzukehren, wenn sie gefunden wurde, aber es sieht so aus, als ob diese Diskussion nicht mehr in der.next()
Dokumentation enthalten ist: api.jquery.com/next.eq(0)
wäre die absolut schnellst, da was ist , was.first()
nennt , aber der Unterschied im Vergleich zu alles andere ist unendlich klein , so dass ich für den lesbaren Code in diesem Fall gehen würde. Davon abgesehen ist mein Beispiel kein Plugin ... wenn die Leute es nicht direkt verwenden, gehen Sie auf jeden Fall mit.eq(0)
.Wie wäre es mit der
first
Methode:jQuery.fn.nextMatching = function(selector) { return this.nextAll(selector).first(); }
quelle
Bearbeiten, aktualisiert
Verwenden der Auswahl für die nächsten Geschwister („vorherige Geschwister“)
jQuery.fn.nextMatching = function nextMatchTest(selector) { return $("~ " + selector, this).first() };
http://jsperf.com/jquery-next-loop-vs-nextall-first/10
Code-Snippet anzeigen
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>
Hinweis: Zum Vergleichstest noch nicht hinzugefügt oder ausprobiert. Nicht sicher, ob tatsächlich effizienter als
.nextAll()
Implementierung. Piece versucht, ein Selector-String-Argument mit mehreren durch Kommas getrennten Zeichen zu analysierenselector
. Gibt das.first()
Element einzelner oder durch Kommas getrennter Selektoren zurück, die als Argument angegeben wurden, oder dasthis
Element, wenn keinselector
Argument angegeben wurde.nextMatchTest()
. Es scheint, als würden bei Chrom 37, dh 11, dieselben Ergebnisse zurückgegebenv2
$.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 };
Code-Snippet anzeigen
$.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>
quelle
~
Kombinator verwendet, funktioniert im IE nicht). (Übrigens,$(selector, context)
wird wahrscheinlich$(context).find(selector)
irgendwann verschwinden und wird sowieso nur konvertiert , so dass die direkte Verwendung etwas schneller ist.) Jsperf.com/jquery-next-loop-vs-nextall-first/11 Aber leider, wenn Selbst der moderne IE schafft es nicht ...selector
nie zuverlässig war und in 1.7 veraltet und in 1.9 entfernt wurde (obwohl es immer noch da ist, ist es Teil der Interna, nur dort zur Unterstützunglive
des Migrations-Plugins).