75 votes

Un moyen efficace et concis de trouver le prochain frère ou sœur correspondant ?

Si l'on s'en tient à l'API officielle de jQuery, existe-t-il un moyen plus concis, mais non moins efficace, de trouver le prochain frère ou la prochaine soeur d'un élément qui correspond à un sélecteur donné, autrement qu'en utilisant la fonction nextAll avec le :first pseudo-classe ?

Quand je parle d'API officielle, je veux dire qu'il ne s'agit pas de pirater les internes, de passer directement à Sizzle, d'ajouter un plug-in dans le mélange, etc. (Si je finis par devoir le faire, qu'il en soit ainsi, mais ce n'est pas l'objet de cette question).

Par exemple, étant donné cette structure :

<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>

Si j'ai un div en this (peut-être dans un click ) et que je veux trouver la prochaine division sœur qui correspond au sélecteur "div.foo", je peux faire ceci :

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

...et cela fonctionne (si je commence par "Cinq", par exemple, il saute "Six" et "Sept" et trouve "Huit" pour moi), mais c'est maladroit et si je veux faire correspondre le premier de n'importe quel sélecteur, cela devient beaucoup plus maladroit. (D'accord, c'est un lot plus concise que ne le serait la boucle DOM brute...)

Je veux essentiellement :

var nextFoo = $(this).nextMatching("div.foo");

...où nextMatching peut accepter toute la gamme des sélecteurs. Je suis toujours surpris que next(selector) ne fait pas ça, mais il ne le fait pas, et les docs sont clairs sur ce qu'il fait, donc...

Je peux toujours l'écrire et l'ajouter, mais si je fais cela et que je m'en tiens à l'API publiée, les choses deviennent plutôt inefficaces. Par exemple, un naïf next boucle :

jQuery.fn.nextMatching = function(selector) {
    var match;

    match = this.next();
    while (match.length > 0 && !match.is(selector)) {
        match = match.next();
    }
    return match;
};

...est nettement plus lent que nextAll("selector:first") . Et ce n'est pas surprenant, nextAll peut confier le tout à Sizzle, et Sizzle a été complètement optimisé. La boucle naïve ci-dessus crée et jette toutes sortes d'objets temporaires et doit ré-analyser le sélecteur à chaque fois, pas étonnant qu'elle soit lente.

Et bien sûr, je ne peux pas juste lancer un :first à la fin :

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

...parce que si cela fonctionne avec des sélecteurs simples comme "div.foo", cela échouera avec l'option "any of several" dont j'ai parlé, comme par exemple "div.foo, div.bar".

Editar : Désolé, j'aurais dû dire : Enfin, je pourrais simplement utiliser .nextAll() et ensuite utiliser .first() sur le résultat, mais alors jQuery devra visiter tous les frères et sœurs juste pour trouver le premier. J'aimerais qu'il s'arrête lorsqu'il obtient une correspondance plutôt que de parcourir toute la liste pour pouvoir jeter tous les résultats sauf le premier. (Bien que cela semble se produire realmente rapide ; voir le dernier cas d'essai dans le comparaison des vitesses lié plus tôt).

Merci d'avance.

92voto

Nick Craver Points 313913

Vous pouvez passer un sélecteur multiple a .nextAll() et utiliser .first() sur le résultat, comme ceci :

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

Editar: Juste à titre de comparaison, le voici ajouté à la suite de tests : http://jsperf.com/jquery-next-loop-vs-nextall-first/2 Cette approche est beaucoup plus rapide parce qu'il s'agit d'une simple combinaison de la remise de l'option .nextAll() en code natif lorsque cela est possible (tous les navigateurs actuels) et en prenant simplement le premier résultat de l'ensemble..... chemin plus rapide que n'importe quel bouclage que vous pouvez faire purement en JavaScript.

9voto

lonesomeday Points 95456

Que diriez-vous d'utiliser le first méthode :

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

1voto

guest271314 Points 2718

Edit, Updated

Utilisation de Sélecteur de frères et sœurs suivants ("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>

Note, pas encore ajouté ou essayé au test de comparaison. Il n'est pas certain qu'il soit réellement plus efficace que .nextAll() implémentation. Ce morceau tente d'analyser un argument de chaîne de sélection comportant plusieurs éléments séparés par des virgules. selector 's . On obtient .first() élément de sélecteurs simples ou séparés par des virgules fournis en argument, ou this si aucun élément selector argument fourni à .nextMatchTest() . Il semble que les mêmes résultats soient obtenus avec chrome 37 et 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
};

$.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>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X