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.