Qual é o método mais rápido para selecionar elementos descendentes em jQuery?

101

Até onde eu sei, existem várias maneiras de selecionar elementos filho no jQuery .

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

Método 1 (usando um escopo)

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

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

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

Método 3 (apenas para crianças imediatas)

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

Método 4 (via seletor CSS) - sugerido por @spinon

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

Método 5 (idêntico ao Método 2 ) - de acordo com @Kai

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

Não estou familiarizado com a criação de perfil para poder investigar isso sozinho, então adoraria ver o que você tem a dizer.

PS: Eu entendo que esta é uma possível duplicata desta pergunta, mas não cobre todos os métodos.

Marko
fonte
Além disso, @spinon - isso é apenas para crianças imediatas? A especificação CSS diz "Corresponde a qualquer elemento F que seja filho de um elemento E."
Marko
7
Você realmente não precisa se preocupar com qual deles é mais rápido (a menos que você esteja fazendo uma grande manipulação de dom) ... jQuery foi construído para ser incrivelmente rápido ...
Reigel
Eu tenho um arquivo HTML de 2 MB, não pergunte como ou por quê :)
Marko
1
Sim. Apenas descendentes de primeiro nível.
spinon
Existe mais uma maneira. $ ("# pai. filho"). show (); que é idêntico ao método # 2. :)
Kai

Respostas:

95

O método 1 e o método 2 são idênticos, com a única diferença é que o método 1 precisa analisar o escopo passado e convertê-lo em uma chamada para $parent.find(".child").show();.

O Método 4 e o Método 5 precisam analisar o seletor e então apenas chamar: $('#parent').children().filter('.child')e $('#parent').filter('.child')respectivamente.

Portanto, o método 3 será sempre o mais rápido porque precisa fazer a menor quantidade de trabalho e usa o método mais direto para obter os filhos do primeiro nível.

Com base nos testes de velocidade revisados ​​de Anurag aqui: http://jsfiddle.net/QLV9y/1/

Teste de velocidade: (mais é melhor)

No Chrome , o Método 3 é o melhor, então o método 1/2 e então 4/5

insira a descrição da imagem aqui

No Firefox , o Método 3 ainda é melhor do que o método 1/2 e 4/5

insira a descrição da imagem aqui

No Opera , o Método 3 ainda é melhor do que o método 4/5 e 1/2

insira a descrição da imagem aqui

No IE 8 , embora seja mais lento em geral do que outros navegadores, ele ainda segue a ordenação do Método 3, 1,2,4,5.

insira a descrição da imagem aqui

No geral, o método 3 é o melhor método geral para usar, pois é chamado diretamente e não precisa percorrer mais de um nível de elementos filho, ao contrário do método 1/2 e não precisa ser analisado como o método 4/5

Porém, tenha em mente que em alguns deles estamos comparando maçãs com laranjas, já que o Método 5 olha para todas as crianças, em vez das de primeiro nível.

Aaron Harun
fonte
Por idêntico, você quer dizer que ambos usam a mesma lógica para pesquisar?
Marko
4
Você não quer dizer que os métodos 1 e 2 são idênticos?
Guffa
Obrigado @Aaron - gostaria de ver o que os outros pensam, aceitarei sua resposta se todos concordarem. Saúde :)
Marko
@JP, quero dizer que é necessário um pouco mais de tempo para reconhecer que um escopo está sendo passado para traduzi-lo no $parent.find(".child");comando.
Aaron Harun
2
@Aaron @Marko - Os testes podem ser um pouco distorcidos, pois estamos sempre usando o nó raiz como contexto, e o documento é muito grande. Apesar disso, estou vendo 1 e 2 alinhados dentro de 20 ops / s um do outro na maioria das execuções. Comparado a 1 e 2, 4 é cerca de 100-200 ops mais lento e 5 é cerca de 400 ops mais lento, o que é compreensível porque passa por todos os descendentes e não apenas pelos filhos. Gráfico - tinyurl.com/25p4dhq
Anurag
13

Método 1

Não pode ser mais curto e mais rápido usando jQuery. Essa chamada desce diretamente para $(context).find(selector)( método 2 , devido à otimização) que, por sua vez, chama getElementById.

Método 2

Está fazendo o mesmo, mas sem algumas chamadas de funções internas desnecessárias.

Método 3

usar children()é mais rápido do que usar find(), mas é claro, children()só encontrará filhos diretos do elemento raiz, ao passo find()que pesquisará recursivamente de cima para baixo em todos os elementos filhos (incluindo sub-elementos filhos)

Método 4

Usar seletores como este tem que ser mais lento. Uma vez sizzleque (que é o mecanismo seletor do jQuery) funciona da direita para a esquerda , ele corresponderá a TODAS as classes .childantes de verificar se são filhos diretos do id 'pai'.

Método 5

Como você afirmou corretamente, esta chamada também criará uma $(context).find(selector)chamada, devido a alguma otimização dentro da jQueryfunção, caso contrário, ela também poderia passar pelo (mais lento) sizzle engine.

Andy
fonte
2
Você não está falando sobre var $ parent = $ ("# parent"), está? Não consigo ver como o Método 1 pode usar getElementById quando o elemento tem uma classe.
Marko
1
Eu queria concordar, mas, no método 1, docs diz, Internally, selector context is implemented with the .find() method-por favor , atualize, eu sei que você se confundiu com os rótulos do OP :)
Reigel
@Reigel: true corrigiu isso. @Marko: parsing #parentrepresenta um id, se for uma classe, getElementByIdobviamente não usará .
jAndy
10

Como é um post antigo, e as coisas mudam com o tempo. Fiz alguns testes nas últimas versões do navegador até agora e estou postando aqui para evitar mal-entendidos.

Usando jQuery 2.1 em navegadores compatíveis com HTML5 e CSS3, o desempenho muda.

Aqui está o cenário e os resultados do teste:

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>

Então, para 100.000 iterações eu recebo:

Estatísticas do seletor JS jQuery

(Eu os adicionei como img para fins de formatação.)

Você mesmo pode executar o snippet de código para testar;)

Vasil Popov
fonte
Oh! Então parece que .find()faz um ótimo trabalho. Continuando a usá-lo. :)
Andrew Surdu