The Selectors API: querySelector and querySelectorAll
By Flavio Copes
Learn how to select DOM elements with querySelector and querySelectorAll, scope a search, loop over results, and safely escape dynamic values.
The DOM gives us two methods that accept CSS selectors:
querySelector()returns the first matching elementquerySelectorAll()returns every matching element
You can call them on document, an element, or a document fragment.
If you want to test a selector first, try the CSS selector tester.
Select the first matching element
Use querySelector() when you need one element:
const heading = document.querySelector('h1')
It returns an Element or null when nothing matches:
const form = document.querySelector('#signup')
if (form) {
form.classList.add('ready')
}
The method returns the first match in document order.
Select every matching element
Use querySelectorAll() when several elements can match:
const buttons = document.querySelectorAll('button')
It returns a static NodeList. The list does not update when matching elements are later added or removed.
You can loop over it with forEach():
buttons.forEach((button) => {
button.disabled = true
})
Or use for...of:
for (const button of buttons) {
button.disabled = true
}
Convert the result to an array when you need array methods that NodeList does not provide:
const labels = [...document.querySelectorAll('.label')]
Use CSS selectors
Both methods accept valid CSS selector strings.
Select by ID:
document.querySelector('#signup')
Select by class:
document.querySelectorAll('.card')
Select by attribute:
document.querySelectorAll('input[required]')
Select with a combinator and a pseudo-class:
document.querySelectorAll('nav > a:not([aria-current])')
You can also pass a comma-separated selector list:
document.querySelectorAll('button, input, select')
CSS pseudo-elements such as ::before do not represent DOM elements, so they do not produce matches.
An invalid selector throws a SyntaxError. It does not return null.
Search inside an element
Call the methods on an element to limit the search to its descendants:
const menu = document.querySelector('#main-menu')
const links = menu.querySelectorAll('a')
Use :scope when the selector should refer to the element itself as the search root:
const directItems = menu.querySelectorAll(':scope > li')
Without :scope, a selector can match based on ancestors outside the element before returning descendants from inside it. :scope makes the intended boundary clear.
Escape dynamic values
HTML id and class values do not have to be valid CSS identifiers.
If a value becomes part of a selector, escape it with CSS.escape():
const id = 'question?1'
const element = document.querySelector(`#${CSS.escape(id)}`)
Do not place an arbitrary string into a selector without escaping it.
When you already know an ID and do not need selector syntax, getElementById() is even clearer:
const element = document.getElementById('signup')
See the MDN references for querySelector(), querySelectorAll(), and CSS.escape().
Related posts about platform: