¿Cuál es el método más rápido para seleccionar elementos descendientes en jQuery?

101

Hasta donde yo sé, hay varias formas de seleccionar elementos secundarios en jQuery .

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

Método 1 (usando un alcance)

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

Método 2 (el método find ())

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

Método 3 (solo para niños inmediatos)

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

Método 4 (a través del selector de CSS) : sugerido por @spinon

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

Método 5 (idéntico al Método 2 ) - según @Kai

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

No estoy familiarizado con la elaboración de perfiles para poder investigar esto por mi cuenta, así que me encantaría ver lo que tiene que decir.

PD: Entiendo que esto es un posible duplicado de esta pregunta, pero no cubre todos los métodos.

Marko
fuente
Además, @spinon, ¿es eso solo para niños inmediatos? La especificación CSS dice "Coincide con cualquier elemento F que sea hijo de un elemento E".
Marko
7
Realmente no tienes que preocuparte de cuál de ellos es más rápido (a menos que estés haciendo una gran manipulación de dom) ... jQuery fue construido para ser increíblemente rápido ...
Reigel
Tengo un archivo HTML de 2 MB, no preguntes cómo ni por qué :)
Marko
1
Si. Solo descendientes de primer nivel.
spinon
Hay una forma más. $ ("# padre. hijo"). show (); que es idéntica a la forma # 2. :)
Kai

Respuestas:

95

El método 1 y el método 2 son idénticos con la única diferencia es que el método 1 necesita analizar el alcance pasado y traducirlo en una llamada a $parent.find(".child").show();.

El método 4 y el método 5 necesitan analizar el selector y luego simplemente llamar a: $('#parent').children().filter('.child')y $('#parent').filter('.child')respectivamente.

Entonces, el método 3 siempre será el más rápido porque necesita hacer la menor cantidad de trabajo y usa el método más directo para obtener niños de primer nivel.

Basado en las pruebas de velocidad revisadas de Anurag aquí: http://jsfiddle.net/QLV9y/1/

Prueba de velocidad: (más es mejor)

En Chrome , el método 3 es el mejor, luego el método 1/2 y luego el 4/5

ingrese la descripción de la imagen aquí

En Firefox , el método 3 sigue siendo mejor que el método 1/2 y luego el 4/5

ingrese la descripción de la imagen aquí

En Opera , el método 3 sigue siendo mejor que el método 4/5 y luego 1/2

ingrese la descripción de la imagen aquí

En IE 8 , aunque es más lento en general que otros navegadores, sigue el método 3, ordenamiento 1,2,4,5.

ingrese la descripción de la imagen aquí

En general, método 3 es el mejor método para usar, ya que se llama directamente y no necesita atravesar más de un nivel de elementos secundarios a diferencia del método 1/2 y no necesita ser analizado como el método 4/5

Sin embargo, tenga en cuenta que en algunos de ellos estamos comparando manzanas con naranjas, ya que el Método 5 analiza a todos los niños en lugar de a los de primer nivel.

Aaron Harun
fuente
¿Por idéntico quiere decir que ambos usan la misma lógica para buscar?
Marko
4
¿No quiere decir que los métodos 1 y 2 son idénticos?
Guffa
Gracias @Aaron - Me gustaría ver lo que piensan los demás, aceptaré tu respuesta si todos están de acuerdo. Saludos :)
Marko
@JP, quiero decir que necesita un poco de tiempo extra para reconocer que se está pasando un alcance para traducirlo en el $parent.find(".child");comando.
Aaron Harun
2
@Aaron @Marko: las pruebas pueden estar un poco sesgadas, ya que siempre usamos el nodo raíz como contexto y el documento es bastante grande. A pesar de eso, veo 1 y 2 alineados dentro de 20 operaciones / seg entre sí en la mayoría de las carreras. En comparación con 1 y 2, 4 es aproximadamente 100-200 operaciones más lento, y 5 es aproximadamente 400 operaciones más lento, lo cual es comprensible porque pasa por todos los descendientes y no solo por los hijos. Gráfico - tinyurl.com/25p4dhq
Anurag
13

Método 1

No puede ser más corto y rápido usando jQuery. Esta llamada se reduce directamente a $(context).find(selector)( método 2 , debido a la optimización) que a su vez, llama getElementById.

Método 2

Está haciendo lo mismo, pero sin algunas llamadas a funciones internas innecesarias.

Método 3

usar children()es más rápido que usar find(), pero, por supuesto, children()solo encontrará hijos directos del elemento raíz, mientras find()que buscará recursivamente de arriba hacia abajo a todos los elementos secundarios (incluidos los elementos secundarios secundarios)

Método 4

El uso de selectores como este tiene que ser más lento. Dado sizzleque (que es el motor de selección de jQuery) funciona de derecha a izquierda , .childprimero coincidirá con TODAS las clases antes de ver si son un hijo directo de id 'parent'.

Método 5

Como indicó correctamente, esta llamada también creará una $(context).find(selector)llamada, debido a alguna optimización dentro de la jQueryfunción, de lo contrario también podría pasar por el (más lento) sizzle engine.

jAndy
fuente
2
No estás hablando de var $ parent = $ ("# parent"), ¿verdad? No puedo ver cómo el Método 1 podría usar getElementById cuando el elemento tiene una clase.
Marko
1
Quería estar de acuerdo pero, en el método 1, los documentos dicen: Internally, selector context is implemented with the .find() method-Actualice, sé que se confundió con las etiquetas del OP :)
Reigel
@Reigel: cierto lo solucionó. @Marko: el análisis #parentrepresenta un id, si es una clase, getElementByIdobviamente no lo usará .
jAndy
10

Como es un post antiguo, y las cosas cambian con el tiempo. Hice algunas pruebas en las últimas versiones del navegador hasta ahora, y las estoy publicando aquí para evitar malentendidos.

Con jQuery 2.1 en navegadores compatibles con HTML5 y CSS3, el rendimiento cambia.

Aquí está el escenario de prueba y los resultados:

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>

Entonces, para 100 000 iteraciones obtengo:

Estadísticas del selector de JS jQuery

(Los he agregado como img para fines de formato).

Puede ejecutar el fragmento de código usted mismo para probar;)

Vasil Popov
fuente
Oh! Entonces parece que .find()hace un gran trabajo. Continuar usándolo. :)
Andrew Surdu