querySelector и querySelectorAll против getElementsByClassName и getElementById в JavaScript

165

Я хотел бы знать, в чем именно разница между querySelectorи querySelectorAllпротив getElementsByClassNameи getElementById?

По этой ссылке я querySelectorмогу узнать, что с помощью я могу написать, document.querySelector(".myclass")чтобы получить элементы с классом myclassи document.querySelector("#myid")получить элемент с идентификатором myid. Но я уже могу это сделать getElementsByClassNameи getElementById. Какой из них должен быть предпочтительным?

Также я работаю в XPages, где идентификатор динамически генерируется с двоеточием и выглядит следующим образом view:_id1:inputText1. Поэтому, когда я пишу, document.querySelector("#view:_id1:inputText1")это не работает. Но написание document.getElementById("view:_id1:inputText1")работает. Есть идеи почему?

Нэвин
источник
1
querySelector используется для запроса дерева HTML DOM, которое может включать html-элемент и его атрибуты в качестве ключевых элементов для запросов ... вы можете использовать регулярное выражение для достижения этого .. dojo.query () делает то же самое
anix
1
Ты имеешь в виду document.querySelectorAll(".myclass")? Использование document.querySelector(".myclass")вернет только первый соответствующий элемент.
mhatch

Ответы:

113

Я хотел бы знать, в чем именно разница между querySelector и querySelectorAll против getElementsByClassName и getElementById?

Синтаксис и поддержка браузера.

querySelector более полезно, когда вы хотите использовать более сложные селекторы.

Например, все элементы списка произошли от элемента, который является членом класса foo: .foo li

document.querySelector ("# view: _id1: inputText1") не работает. Но запись document.getElementById ("view: _id1: inputText1") работает. Есть идеи почему?

Символ :имеет особое значение внутри селектора. Вы должны избежать этого. (Символ побега селектора имеет особое значение в строке JS, так что вы должны бежать , что тоже).

document.querySelector("#view\\:_id1\\:inputText1")
Quentin
источник
3
Это будет варьироваться от браузера к браузеру (и от версии к версии). Я предположил бы, что основанные на селекторе были более дорогими (но не таким способом, который когда-либо будет существенным)
Квентин
1
Я поддерживаю заявление @ janaspage. Сайт не работает и сегодня.
Доплуми
6
А о выборе класса см. Также jsperf.com/getelementsbyclassname-vs-queryselectorall/25 . Вывод: гораздо лучше отдавать предпочтение чистому javascript, чем jquery, а также конкретным функциям getElementByIdи getElementsByClassName. Без выбора className может быть несколько сотен раз медленнее getElementsByClassName.
Atrahasis
101

Сбор от Mozilla Документация:

Интерфейс NodeSelector Эта спецификация добавляет два новых метода к любым объектам, реализующим интерфейсы Document, DocumentFragment или Element:

querySelector

Возвращает первый соответствующий элементный элемент в поддереве узла. Если соответствующий узел не найден, возвращается ноль.

querySelectorAll

Возвращает NodeList, содержащий все совпадающие узлы Element в поддереве узла, или пустой NodeList, если совпадений не найдено.

и

Примечание. Возвращаемый NodeList querySelectorAll()не является активным , что означает, что изменения в DOM не отражаются в коллекции. Это отличается от других методов запроса DOM, которые возвращают списки живых узлов.

diEcho
источник
32
+1 за указание на различие в списке живых узлов. Это очень важное различие, которое нужно знать, в зависимости от того, как вы собираетесь использовать результаты.
jmbpiano
7
«живой» означает узел, добавленный во время выполнения DOM, и может работать на этом добавленном узле
Ньюли
83

Что касается различий, в результатах между querySelectorAllи есть важный аспект getElementsByClassName: возвращаемое значение отличается. querySelectorAllвернет статическую коллекцию, а getElementsByClassNameвернет живую коллекцию. Это может привести к путанице, если вы сохраните результаты в переменной для дальнейшего использования:

  • Сгенерированная с помощью переменной querySelectorAllбудет содержать элементы, которые выполнили селектор в момент вызова метода .
  • Сгенерированная с помощью переменной getElementsByClassNameбудет содержать элементы, которые выполнили селектор, когда он используется (который может отличаться от момента вызова метода).

Например, обратите внимание, что даже если вы не переназначили переменные aux1и aux2они содержат разные значения после обновления классов:

// storing all the elements with class "blue" using the two methods
var aux1 = document.querySelectorAll(".blue");
var aux2 = document.getElementsByClassName("blue");

// write the number of elements in each array (values match)
console.log("Number of elements with querySelectorAll = " + aux1.length);
console.log("Number of elements with getElementsByClassName = " + aux2.length);

// change one element's class to "blue"
document.getElementById("div1").className = "blue";

// write the number of elements in each array (values differ)
console.log("Number of elements with querySelectorAll = " + aux1.length);
console.log("Number of elements with getElementsByClassName = " + aux2.length);
.red { color:red; }
.green { color:green; }
.blue { color:blue; }
<div id="div0" class="blue">Blue</div>
<div id="div1" class="red">Red</div>
<div id="div2" class="green">Green</div>

Альваро Монторо
источник
2
Просто упомянуть - все старые API DOM возвращают список узлов, а именно document.getElementsByName, document.getElementsByTagNameNSили document.getElementsByTagNameбудут демонстрировать такое же поведение.
RBT
2
Некоторые анализы говорят, что querySelector занимает больше времени, чем getElementById, как здесь dimlucas.com/index.php/2016/09/17/… . Что, если мы примем во внимание время доступа? Неужели живой узел, полученный из getElementById, занимает больше времени, чем статический узел из querySelector?
Эрик
1
@RBT Я бы упомянул, что эти старые DOM API не возвращают объекты NodeList, они возвращают HTMLCollections.
Miscreant
@Eric document.getElementById()не возвращает живой узел. Это быстрее, чем, document.querySelector('#id_here')вероятно, потому что querySelectorсначала нужно будет проанализировать селектор CSS.
Miscreant
68

Для этого ответа, я имею в виду querySelectorи , querySelectorAllкак querySelector * и getElementById, getElementsByClassName, getElementsByTagName, и , getElementsByNameкак getElement *.

Основные отличия

  1. querySelector * является более гибким, так как вы можете передать ему любой селектор CSS3, а не только простые для id, тега или класса.
  2. Производительность querySelector изменяется в зависимости от размера DOM, для которого он вызывается. * Чтобы быть точным, вызовы querySelector * выполняются за время O (n), а вызовы getElement * выполняются за время O (1), где n - общее количество всех дочерних элементов элемента или документа, для которых он был вызван. Этот факт кажется наименее известным, поэтому я обдумываю его.
  3. Вызовы getElement * возвращают прямые ссылки на DOM, тогда как querySelector * внутренне делает копии выбранных элементов перед возвратом ссылок на них. Они называются «живыми» и «статическими» элементами. Это НЕ строго связано с типами, которые они возвращают. Я не знаю, как определить, является ли элемент активным или статическим программно, поскольку это зависит от того, был ли элемент скопирован в какой-то момент и не является внутренним свойством данных. Изменения в живых элементах применяются немедленно - изменение активного элемента изменяет его непосредственно в DOM, и поэтому следующая строка JS может увидеть это изменение, и оно распространяется на любые другие активные элементы, ссылающиеся на этот элемент немедленно. Изменения в статических элементах записываются обратно в DOM только после выполнения текущего скрипта.
  4. Типы возврата этих вызовов различаются. querySelectorи getElementByIdоба возвращают один элемент. querySelectorAllи getElementsByNameоба возвращают NodeLists, будучи более новыми функциями, которые были добавлены после того, как HTMLCollection вышел из моды. Старые getElementsByClassNameи getElementsByTagNameоба возвращают HTMLCollections. Опять же, это по существу не имеет отношения к тому, являются ли элементы живыми или статичными.

Эти понятия обобщены в следующей таблице.

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)

Подробности, советы и примеры

  • HTMLCollections не так массивен, как NodeLists, и не поддерживает .forEach (). Я считаю, что оператор распространения полезен, чтобы обойти это:

    [...document.getElementsByClassName("someClass")].forEach()

  • Каждый элемент, и глобальный document, имеют доступ ко всем этим функциям, кроме getElementByIdи getElementsByName, которые реализованы только в document.

  • Цепочка вызовов getElement * вместо использования querySelector * улучшит производительность, особенно на очень больших DOM. Даже на небольших DOM и / или с очень длинными цепями это обычно быстрее. Однако, если вы не знаете, что вам нужна производительность, читаемость querySelector * должна быть предпочтительной. querySelectorAllчасто труднее переписать, потому что вы должны выбирать элементы из NodeList или HTMLCollection на каждом шаге. Например, следующий код не работает:

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`.
  • Поскольку все элементы имеют доступ как к вызовам querySelector *, так и к getElement *, вы можете создавать цепочки, используя оба вызова, что может быть полезно, если вы хотите повысить производительность, но не можете избежать запроса querySelector, который нельзя записать в терминах вызовов getElement *. ,

  • Хотя, как правило, легко определить, можно ли писать селектор, используя только вызовы getElement *, есть один случай, который может быть неочевидным:

    document.querySelectorAll(".class1.class2")

    можно переписать как

    document.getElementsByClassName("class1 class2")

  • Использование getElement * для статического элемента, извлеченного с помощью querySelector *, приведет к тому, что элемент будет действителен по отношению к статическому подмножеству DOM, скопированному с помощью querySelector, но не будет действителен по отношению к полному DOM документа ... вот где простое Живая / статическая интерпретация элементов начинает разваливаться. Вам, вероятно, следует избегать ситуаций, когда вам нужно беспокоиться об этом, но если вы это сделаете, помните, что querySelector * вызывает элементы копирования, которые они находят, прежде чем возвращать ссылки на них, но getElement * вызывает выборку прямых ссылок без копирования.

  • Ни один API не указывает, какой элемент должен быть выбран первым, если есть несколько совпадений.

  • Поскольку querySelector * выполняет итерацию по DOM до тех пор, пока не найдет совпадение (см. Основное отличие № 2), вышеприведенное также подразумевает, что вы не можете полагаться на положение элемента, который вы ищете в DOM, чтобы гарантировать его быстрое нахождение - Браузер может перебирать DOM назад, вперед, сначала в глубину, в ширину или другим способом. getElement * все равно будет находить элементы примерно в одинаковое время независимо от их размещения.

Тимофей 'Саша' Кондрашов
источник
4
Безусловно самый точный ответ на эту тему. Должен быть проголосовал больше.
SeaWarrior404
очень точная запись должна быть в вашем блоге, Саша
theking2
25

Я пришел на эту страницу исключительно для того, чтобы выяснить, какой метод лучше использовать с точки зрения производительности, т. Е. Какой из них быстрее:

querySelector / querySelectorAll or getElementsByClassName

и я нашел это: https://jsperf.com/getelementsbyclassname-vs-queryselectorall/18

Он запускает тест на 2х приведенных выше примерах, а также тест на эквивалентный селектор jQuery. мои результаты испытаний были следующими:

getElementsByClassName = 1,138,018 operations / sec - <<< clear winner
querySelectorAll = 39,033 operations / sec
jquery select = 381,648 operations / sec
OTurner
источник
1
Вау, это огромная разница, спасибо за поиск. Очевидно, что querySelectorAllтребуется дополнительная работа за кулисами (в том числе анализ выражения селектора, учет псевдоэлементов и т. Д.), Хотя getElementsByClassNameэто всего лишь рекурсивный обход объекта.
Джон Вайс
18

querySelector может быть полным CSS (3) -выборщиком с идентификаторами, классами и псевдоклассами вместе, например так:

'#id.class:pseudo'

// or

'tag #id .class .class.class'

с помощью которого getElementByClassNameвы можете просто определить класс

'class'

с помощью которого getElementByIdвы можете просто определить идентификатор

'id'
Algorhythm
источник
1
Является ли :firstселектор CSS, теперь? :first-classили, :first-of-typeможет быть, но я подумал, что :firstэто дополнение JavaScript / jQuery / Sizzle.
Дэвид говорит восстановить Монику
@DavidThomas Да, это часть CSS3. Его можно использовать так: css-tricks.com/almanac/selectors/f/first-child
алгоритм
2
но :first, заметно, нет :first-child.
Дэвид говорит восстановить Монику
3
«Авторам рекомендуется, что, хотя использование псевдоэлементов в селекторах разрешено, они не будут соответствовать никаким элементам в документе и, следовательно, не приведут к возврату каких-либо элементов. Поэтому авторам рекомендуется избегать использования псевдоэлементов. элементы в селекторах, которые передаются методам, определенным в этой спецификации. " w3.org/TR/selectors-api/#grammar
богатый ремер
Кроме того, в IE (конечно) есть ошибка, которая заставляет его возвращать корневой элемент html вместо пустого списка элементов при выборе псевдоэлементов.
богатый ремер
7

querySelectorи querySelectorAllявляются относительно новыми API, тогда как getElementByIdи getElementsByClassNameбыли с нами намного дольше. Это означает, что то, что вы используете, будет в основном зависеть от того, какие браузеры вам нужно поддерживать.

Что касается :, он имеет особое значение, поэтому вы должны избегать его, если вы должны использовать его как часть идентификатора / имени класса.

Ян Ханчич
источник
13
Это не обязательно правда. Например, querySelectorAllдоступно в IE8, а getElementsByClassNameнет.
DaveJ
querySelectorAll... в основном все: caniuse.com/#search=querySelectorAll
dsdsdsdsd
1
@Naveen getelementsbyclassname vs querySelectorAll vs jquery select может быть полезным.
lowtechsun
5

querySelectorимеет w3c Selector API

getElementByимеет w3c DOM API

ИМО самая заметная разница в том, что тип возвращаемого значения querySelectorAll представляет собой статический список узлов, а для getElementsByнего - список активных узлов. Поэтому цикл в демо 2 никогда не заканчивается, потому что lisон активен и обновляется во время каждой итерации.

// Demo 1 correct
var ul = document.querySelectorAll('ul')[0],
    lis = ul.querySelectorAll("li");
for(var i = 0; i < lis.length ; i++){
    ul.appendChild(document.createElement("li"));
}

// Demo 2 wrong
var ul = document.getElementsByTagName('ul')[0], 
    lis = ul.getElementsByTagName("li"); 
for(var i = 0; i < lis.length ; i++){
    ul.appendChild(document.createElement("li")); 
}
mzoz
источник
4

Разница между "querySelector" и "querySelectorAll"

//querySelector returns single element
let listsingle = document.querySelector('li');
console.log(listsingle);


//querySelectorAll returns lit/array of elements
let list = document.querySelectorAll('li');
console.log(list);


//Note : output will be visible in Console
<ul>
<li class="test">ffff</li>
<li class="test">vvvv</li>
<li>dddd</li>
<li class="test">ddff</li>
</ul>

Амбудж Ханна
источник
2

посмотри на это

https://codepen.io/bagdaulet/pen/bzdKjL

getElementById быстрее, чем querySelector на 25%

JQuery является самым медленным

var q = time_my_script(function() {

    for (i = 0; i < 1000000; i++) {
         var w = document.querySelector('#ll');
    }

});

console.log('querySelector: '+q+'ms');
Багдаулет Сайын
источник
-3

Основное различие между querySelector и getlementbyID (Claassname, Tagname и т. Д.) Состоит в том, что если существует более одного элемента, удовлетворяющего условию, то querySelector вернет только один выход, тогда как getElementBy * вернет все элементы.

Давайте рассмотрим пример, чтобы сделать его более понятным.

 <nav id="primary" class="menu">
                            <a class="link" href="#">For Business</a>
                            <a class="link" href="#">Become an Instructor</a>
                            <a class="link" href="#">Mobile Applications</a>
                            <a class="link" href="#">Support</a>
                            <a class="link" href="#">Help</a>
   </nav> 

Ниже код объяснит разницу

**QUERY SELECTOR**
document.querySelector('.link'); // Output : For Business (element)

document.querySelectorAll('.link'); //Out All the element with class link

**GET ELEMENT**
document.getElementsByClassName('link') // Output : will return all the element with a class "link" but whereas in query selector it will return only one element which encounters first.

Inshort, если мы хотим выбрать один элемент, перейти к Queryslector или если мы хотим, чтобы несколько элементов перейти к getElement

аджай верма
источник
1
getElementById возвращает только один элемент, между ними нет разницы.
Тимофей 'Саша' Кондрашов