Para esta resposta, refiro-me querySelectore querySelectorAllcomo querySelector * e getElementById, getElementsByClassName, getElementsByTagName, e getElementsByNamecomo GetElement *.
Principais diferenças
- querySelector * é mais flexível, pois você pode passar para ele qualquer seletor CSS3, não apenas os simples para identificação, tag ou classe.
- O desempenho do querySelector muda com o tamanho do DOM no qual é chamado. * Para ser mais preciso, as chamadas querySelector * são executadas em O (n) tempo e as chamadas getElement * são executadas em O (1), em que n é o número total de todos os filhos do elemento ou documento em que é chamado. Esse fato parece ser o menos conhecido, então estou em negrito.
- As chamadas getElement * retornam referências diretas ao DOM, enquanto o querySelector * faz internamente cópias dos elementos selecionados antes de retornar referências a eles. Estes são referidos como elementos "ao vivo" e "estáticos". Isso NÃO está estritamente relacionado aos tipos que eles retornam. Não há como saber se um elemento está ativo ou estático programaticamente, pois depende se o elemento foi copiado em algum momento e não é uma propriedade intrínseca dos dados. As alterações nos elementos ativos são aplicadas imediatamente - a alteração de um elemento ativo o altera diretamente no DOM e, portanto, a próxima linha de JS pode ver essa alteração, e se propaga para qualquer outro elemento ativo que faça referência a esse elemento imediatamente. Alterações nos elementos estáticos são gravadas apenas no DOM após a execução do script atual.
- Os tipos de retorno dessas chamadas variam.
querySelectore getElementByIdambos retornam um único elemento. querySelectorAlle getElementsByNameambos retornam NodeLists, sendo funções mais recentes que foram adicionadas depois que o HTMLCollection saiu de moda. Os mais antigos getElementsByClassNamee os getElementsByTagNamedois retornam HTMLCollections. Novamente, isso é essencialmente irrelevante para saber se os elementos são vivos ou estáticos.
Esses conceitos estão resumidos na tabela a seguir.
Function | Live? | Type | Time Complexity
querySelector | N | Element | O(n)
querySelectorAll | N | NodeList | O(n)
getElementById | Y | Element | O(1)
getElementsByClassName | Y | HTMLCollection | O(1)
getElementsByTagName | Y | HTMLCollection | O(1)
getElementsByName | Y | NodeList | O(1)
Detalhes, dicas e exemplos
As HTMLCollections não são tão parecidas com matrizes quanto as NodeLists e não suportam .forEach (). Acho o operador spread útil para contornar isso:
[...document.getElementsByClassName("someClass")].forEach()
Todos os elementos e o global documenttêm acesso a todas essas funções, exceto getElementByIde getElementsByName, as quais são implementadas apenas document.
Encadear chamadas getElement * em vez de usar querySelector * melhorará o desempenho, especialmente em DOMs muito grandes. Mesmo em DOMs pequenos e / ou com cadeias muito longas, geralmente é mais rápido. No entanto, a menos que você saiba que precisa do desempenho, a legibilidade do querySelector * deve ser preferida. querySelectorAllgeralmente é mais difícil reescrever, porque você deve selecionar elementos do NodeList ou HTMLCollection a cada etapa. Por exemplo, o código a seguir não funciona:
document.getElementsByClassName("someClass").getElementsByTagName("div")
because you can only use getElements* on single elements, not collections. For example:
`document.querySelector("#someId .someClass div")`
could be written as:
document.getElementById("someId").getElementsByClassName("someClass")[0].getElementsByTagName("div")[0]
Note the use of `[0]` to get just the first element of the collection at each step that returns a collection, resulting in one element at the end just like with `querySelector`.
Como todos os elementos têm acesso às chamadas querySelector * e getElement *, você pode criar cadeias usando as duas chamadas, o que pode ser útil se você quiser obter algum ganho de desempenho, mas não pode evitar um querySelector que não possa ser gravado em termos das chamadas getElement * .
Embora seja geralmente fácil saber se um seletor pode ser gravado usando apenas chamadas getElement *, há um caso que pode não ser óbvio:
document.querySelectorAll(".class1.class2")
pode ser reescrito como
document.getElementsByClassName("class1 class2")
O uso de getElement * em um elemento estático buscado com querySelector * resultará em um elemento que está ativo com relação ao subconjunto estático do DOM copiado por querySelector, mas não com relação ao documento completo DOM ... é aqui que o simples a interpretação ao vivo / estática dos elementos começa a desmoronar. Você provavelmente deve evitar situações em que precisa se preocupar com isso, mas, se o fizer, lembre-se de que querySelector * chama elementos de cópia que eles encontram antes de retornar referências a eles, mas as chamadas getElement * buscam referências diretas sem copiar.
Nenhuma API especifica qual elemento deve ser selecionado primeiro se houver várias correspondências.
Como o querySelector * itera pelo DOM até encontrar uma correspondência (consulte a Diferença principal nº 2), o exposto acima também implica que você não pode confiar na posição de um elemento que está procurando no DOM para garantir que ele seja encontrado rapidamente. o navegador pode percorrer o DOM para trás, para frente, profundidade primeiro, largura primeiro ou de outra forma. O getElement * ainda encontrará elementos aproximadamente na mesma quantidade de tempo, independentemente de seu posicionamento.