Quelle est la méthode la plus rapide pour sélectionner les éléments descendants dans jQuery?

101

Autant que je sache, il existe un certain nombre de façons de sélectionner des éléments enfants dans jQuery .

//Store parent in a variable  
var $parent = $("#parent");

Méthode 1 (en utilisant une portée)

$(".child", $parent).show();

Méthode 2 (la méthode find ())

$parent.find(".child").show();

Méthode 3 (pour les enfants immédiats uniquement)

$parent.children(".child").show();

Méthode 4 (via le sélecteur CSS) - suggérée par @spinon

$("#parent > .child").show();

Méthode 5 (identique à la méthode 2 ) - selon @Kai

$("#parent .child").show();

Je ne suis pas familier avec le profilage pour pouvoir enquêter moi-même sur cette question, je serais donc ravi de voir ce que vous avez à dire.

PS Je comprends que c'est une duplication possible de cette question mais elle ne couvre pas toutes les méthodes.

Marko
la source
De plus, @spinon - est-ce uniquement pour les enfants immédiats? La spécification CSS dit "Correspond à tout élément F qui est un enfant d'un élément E."
Marko
7
Vous n'avez pas vraiment à vous soucier de ce qui est le plus rapide (à moins que vous ne fassiez une très grosse manipulation de dom) ... jQuery a été conçu pour être génial rapidement ...
Reigel
J'ai un fichier HTML de 2 Mo, ne me demandez pas comment ni pourquoi :)
Marko
1
Oui. Descendants de premier niveau uniquement.
spinon
Il y a un autre moyen. $ ("# parent .child"). show (); qui est identique à la voie n ° 2. :)
Kai

Réponses:

95

La méthode 1 et la méthode 2 sont identiques à la seule différence que la méthode 1 doit analyser la portée passée et la traduire en un appel à $parent.find(".child").show();.

La méthode 4 et la méthode 5 doivent toutes deux analyser le sélecteur, puis simplement appeler: $('#parent').children().filter('.child')et $('#parent').filter('.child')respectivement.

Ainsi, la méthode 3 sera toujours la plus rapide car elle nécessite le moins de travail et utilise la méthode la plus directe pour obtenir des enfants de premier niveau.

Basé sur les tests de vitesse révisés d'Anurag ici: http://jsfiddle.net/QLV9y/1/

Test de vitesse: (Plus c'est mieux)

Sur Chrome , la méthode 3 est la meilleure, puis la méthode 1/2 puis 4/5

entrez la description de l'image ici

Sur Firefox , la méthode 3 est toujours meilleure que la méthode 1/2 puis 4/5

entrez la description de l'image ici

Sur Opera , la méthode 3 est toujours la meilleure, puis la méthode 4/5 puis 1/2

entrez la description de l'image ici

Sur IE 8 , bien que globalement plus lent que les autres navigateurs, il suit toujours la méthode 3, 1, 2, 4, 5 et 5.

entrez la description de l'image ici

Dans l'ensemble, la méthode 3 est la meilleure méthode globale à utiliser car elle est appelée directement et elle n'a pas besoin de traverser plus d'un niveau d'éléments enfants contrairement à la méthode 1/2 et elle n'a pas besoin d'être analysée comme la méthode 4/5

Cependant, gardez à l'esprit que dans certains d'entre eux, nous comparons des pommes à des oranges, car la méthode 5 concerne tous les enfants au lieu de ceux de premier niveau.

Aaron Harun
la source
Par identique vous voulez dire qu'ils utilisent tous les deux la même logique pour rechercher?
Marko
4
Ne voulez-vous pas dire que les méthodes 1 et 2 sont identiques?
Guffa
Merci @Aaron - j'aimerais voir ce que les autres pensent, j'accepterai votre réponse si tout le monde est d'accord. Cheers :)
Marko
@JP, je veux dire qu'il faut un peu plus de temps pour reconnaître qu'une portée est passée pour la traduire dans la $parent.find(".child");commande.
Aaron Harun
2
@Aaron @Marko - Les tests peuvent être un peu biaisés car nous utilisons toujours le nœud racine comme contexte, et le document est assez volumineux. Malgré cela, je vois 1 et 2 s'aligner dans les 20 ops / s l'un de l'autre sur la plupart des courses. Par rapport à 1 et 2, 4 est environ 100-200 ops plus lent, et 5 environ 400 ops plus lent, ce qui est compréhensible car il passe par tous les descendants et pas seulement les enfants. Graphique - tinyurl.com/25p4dhq
Anurag
13

Méthode 1

Vous ne pouvez pas être plus court et plus rapide avec jQuery. Cet appel descend directement à $(context).find(selector)( méthode 2 , en raison de l'optimisation) qui à son tour, appelle getElementById.

Méthode 2

Fait la même chose, mais sans appels de fonction internes inutiles.

Méthode 3

l'utilisation children()est plus rapide que l'utilisation find(), mais bien sûr, children()ne trouvera que les enfants directs de l'élément racine alors que la find()recherche récursive de haut en bas vers tous les éléments enfants (y compris les sous-éléments enfants)

Méthode 4

L'utilisation de sélecteurs comme celui-ci doit être plus lente. Puisque sizzle(qui est le moteur de sélection de jQuery) fonctionne de droite à gauche , il correspondra à TOUTES les classes .childavant de regarder si elles sont un enfant direct de l'id 'parent'.

Méthode 5

Comme vous l'avez indiqué correctement, cet appel créera également un $(context).find(selector)appel, en raison d'une certaine optimisation dans la jQueryfonction, sinon il pourrait également passer par le (plus lent) sizzle engine.

jAndy
la source
2
Vous ne parlez pas du var $ parent = $ ("# parent") n'est-ce pas? Je ne vois pas comment la méthode 1 pourrait utiliser getElementById lorsque l'élément a une classe?
Marko
1
Je voulais être d'accord mais, dans la méthode 1, la documentation dit, Internally, selector context is implemented with the .find() method-s'il vous plaît mettre à jour, je sais que vous avez été confus sur les étiquettes de l'OP :)
Reigel
@Reigel: true a corrigé cela. @Marko: l'analyse #parentreprésente un identifiant, si c'est une classe, il ne l'utilisera getElementByIdévidemment pas.
jAndy
10

Comme c'est un ancien post, et les choses changent avec le temps. J'ai fait quelques tests sur les dernières versions du navigateur jusqu'à présent, et je le publie ici pour éviter les malentendus.

En utilisant jQuery 2.1 sur des navigateurs compatibles HTML5 et CSS3, les performances changent.

Voici le scénario de test et les résultats:

function doTest(selectorCallback) {
    var iterations = 100000;

    // Record the starting time, in UTC milliseconds.
    var start = new Date().getTime();

    for (var i = 0; i < iterations; i++) {
        // Execute the selector. The result does not need to be used or assigned
        selectorCallback();
    }

    // Determine how many milliseconds elapsed and return
    return new Date().getTime() - start;
}

function start() {
    jQuery('#stats').html('Testing...');
    var results = '';

    results += "$('#parent .child'): " + doTest(function() { jQuery('#parent .child'); }) + "ms";
    results += "<br/>$('#parent > .child'): " + doTest(function() { jQuery('#parent > .child'); }) + "ms";
    results += "<br/>$('#parent').children('.child'): " + doTest(function() { jQuery('#parent').children('.child'); }) + "ms";
    results += "<br/>$('#parent').find('.child'): " + doTest(function() { jQuery('#parent').find('.child'); }) + "ms";
    $parent = jQuery('#parent');
    results += "<br/>$parent.find('.child'): " + doTest(function() { $parent.find('.child'); }) + "ms";

    jQuery('#stats').html(results);
}
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=7, IE=8, IE=9, chrome=1" />
    <title>HTML5 test</title>
    <script src="//code.jquery.com/jquery-2.1.1.js"></script>
</head>
<body>

<div id="stats"></div>
<button onclick="start()">Test</button>

<div>
    <div id="parent">
        <div class="child"></div>
        <div class="child"></div>
        <div class="child"></div>
        <div class="child"></div>
        <div class="child"></div>
        <div class="child"></div>
        <div class="child"></div>
    </div>
</div>

</body>
</html>

Donc, pour 100 000 itérations, j'obtiens:

Statistiques du sélecteur JS jQuery

(Je les ai ajoutés en tant qu'img à des fins de formatage.)

Vous pouvez exécuter l'extrait de code vous-même pour tester;)

Vasil Popov
la source
Oh! Alors semble .find()faire un excellent travail. Continuer à l'utiliser. :)
Andrew Surdu