return elements that match specified CSS selector(s)

The querySelectorAll() Method – Basics and Syntax

The querySelectorAll() method is a built-in JavaScript function (on the Document and Element objects) that accepts a CSS selector string and returns a **static NodeList** of all matching elements in the document (or within a given element) [1]. In other words, it finds all elements that match the selector(s) and gives you a snapshot list.

Syntax

document.querySelectorAll(selectors)
  • selectors: A string of one or more valid CSS selectors (e.g., "div.note, #main > p.highlighted") [2].

  • Return value: A non-live (static) NodeList containing each matching Element (or an empty NodeList if no matches) [3].

If the selector string is invalid, querySelectorAll() will throw a SyntaxError exception [4].

For example, to select all paragraph (<p>) elements, you can write:

const paragraphs = document.querySelectorAll("p");

This returns a NodeList of every <p> in the document [5]. The order of elements in the list is document order (parents before children, earlier siblings before later siblings) [3]. Because the list is static, it will not automatically update if the DOM changes later; to get fresh results you would call querySelectorAll() again.


Usage and Examples

querySelectorAll() is extremely flexible because it accepts any valid CSS selector. Some common use cases and examples include:

Selecting by Tag Name

const items = document.querySelectorAll("li"); // Finds all <li> elements [5].

Selecting by Class or ID (using CSS syntax)

const mainSection = document.querySelectorAll("#main");          // Select element(s) with id="main"
const buttons = document.querySelectorAll(".btn.primary");      // Select all elements with classes "btn" and "primary"

Note: You can combine selectors with commas to match multiple criteria. For example:

const notesAndAlerts = document.querySelectorAll("div.note, div.alert");
// Returns all <div> elements that have class note or class alert [6].

Selecting Nested or Child Elements

You can use descendant or child selectors for fine-grained selection:

const highlightedParas = document.querySelectorAll("#test div.highlighted > p");

In this example, the code finds <p> elements whose immediate parent is a <div class="highlighted"> inside the container with id="test" [7]. This shows how you can scope selectors to a specific element.

Attribute Selectors and More Complex Queries

const iframes = document.querySelectorAll("iframe[data-src]");
// Selects all <iframe> elements that have a data-src attribute [8].

const activeItems = document.querySelectorAll("#user-list li[data-active='1']");
// Finds all <li> elements inside #user-list that have data-active="1" [9].

Looping Over the NodeList

Once you have the NodeList, you can loop over it just like an array. Modern NodeLists support forEach:

const items = document.querySelectorAll(".item");
items.forEach(item => {
    // Do something with each item
    item.style.color = "blue";
});

MDN demonstrates this usage: highlightedItems.forEach(userItem => { deleteUser(userItem); }); [10]. (If older browsers are a concern, you can convert the NodeList to an array with Array.from() or spread syntax before using array methods.)


Browser Compatibility

querySelectorAll() is widely supported across all modern browsers and has been part of web standards since DOM Level 3 (around 2004) [11]. Today it works in essentially every major browser and platform.

Browser Support
Chrome Supported from version 4 (and all current versions) [12].
Firefox Supported from 3.5 (with partial or no support in FF2-3) [13].
Safari Supported from Safari 3.1 onward [14].
Edge Supported from the very first Edge 12 version onward [15].
Opera Supported from Opera 10 onward [16].
Internet Explorer IE9 and newer: Fully supported (in standards mode) [17]. IE8: Partial support (standards mode, limited to CSS 2.1 selectors) [17]. IE7 and below do not support it.
Mobile Browsers All modern mobile browsers (iOS Safari, Chrome for Android, etc.) support querySelectorAll [18] [19].

Because support is nearly universal, you can safely use querySelectorAll in most code without polyfills.


Performance Comparison

Performance can vary depending on browser and context. The trade-offs are often between the speed of specialized, simpler methods (which return live collections) and the flexibility of querySelectorAll (which returns a static collection).

Speed of Simple vs. Complex Queries

  • Simple Queries (by ID or single class): Generally fastest using the specialized methods like getElementById, getElementsByClassName, or getElementsByTagName [20] [21]. For example, getElementById("foo") can be slightly faster than document.querySelector("#foo") [21].
  • Complex CSS Selectors: querySelectorAll excels here because it can handle any valid CSS query (nested, grouped, attribute-based, etc.), but this generality comes with some overhead [20].
  • Large Collections: For creating a large collection by class or tag name, the specialized methods are often orders of magnitude faster. Example: Selecting 5,000 elements in a loop showed getElementsByTagName("p") to be significantly faster than querySelectorAll("p") [22].

Accessing Elements in the Result

Interestingly, once the collection is obtained, accessing individual items by index can be faster with the static NodeList (from querySelectorAll) than with a live HTMLCollection (from older methods) [23]. This is because the static list is a snapshot, whereas a live collection may have to re-validate its contents when accessed.

Summary of Best Tool for the Job

Method Strength
getElementById Fastest for finding a single element by ID [20] [21].
getElementsByClassName / getElementsByTagName Faster at creating large collections of elements by class or tag name [20] [22]. Returns a live collection.
querySelector / querySelectorAll Most versatile (supports any CSS selector). Can be slower on large repeated searches, but results can be accessed quickly [25] [23]. Returns a static collection.

Real-world impact: In practice, performance differences are usually negligible unless you are doing massive or repeated DOM queries. The consensus is to use the method that makes your code clearest.


Caveats and Best Practices

Static vs. Live List

The returned NodeList is static. It will not update if the DOM changes after the query is run (e.g., if elements are added or removed) [26]. If you need a fresh list, you must call querySelectorAll() again. In contrast, getElementsByClassName returns a live HTMLCollection that automatically updates.

CSS Selector Syntax

The string must be a valid CSS selector. Special characters in IDs or classes may need escaping. A malformed selector will throw a SyntaxError [4]. Also, pseudo-elements (like ::before) are not real DOM elements and will return an empty list [27].

Not a Real Array

A NodeList is array-like (it has length, supports indexing, and modern ones have forEach [10]). However, it's not a true Array. If you need array methods like map or filter (especially on older browsers), you must convert it: const arr = Array.from(nodeList);.

Scope to a Container

You can call querySelectorAll on any Element, not just document. This scopes the search to that element's subtree.

const sidebar = document.getElementById("sidebar");
const links = sidebar.querySelectorAll("a.active"); // Finds only <a class="active"> inside #sidebar [7].

Cache Results

If you plan to reuse the query results, **store the result in a variable**. Do not call querySelectorAll() repeatedly inside a loop [24].

const items = document.querySelectorAll(".item"); // Run query once
items.forEach(item => {
    // expensive DOM work on item
});

In summary, querySelectorAll() is a powerful and convenient DOM selection method with broad browser support [17]. It excels when using complex CSS-style queries and handling multiple results. Just be mindful of its static result list and choose the right tool for your specific task.


Citations

Sources: Authoritative documentation and benchmarks from MDN and community sources [1] [28] [29] [17] [20] [30] [31].