Skip to content
FLAVIO COPES
flaviocopes.com
2026

The Selectors API: querySelector and querySelectorAll

By

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:

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().

Tagged: Web Platform ยท All topics
~~~

Related posts about platform: