Css Selectors Reference

Comprehensive reference for css selectors reference covering essential concepts, practical examples, and production best practices.

Css Selectors Reference

This page is a practical reference for writing, debugging, and troubleshooting CSS selectors in engineering contexts: browser devtools, DOM scripting, test automation, and CSS-in-JS workflows. It matters whenever you need to target elements without adding IDs or modifying markup—whether you're building a one-off script, writing automated tests, or overriding styles in a compiled stylesheet. The following sections are organised by what you’re trying to accomplish, not by selector taxonomy, and include exact commands, flags, code snippets, and the error text you’ll actually see when things go wrong.

Selecting Elements in JavaScript

document.querySelector() and document.querySelectorAll()

These are the most common entry points for CSS selectors in browser scripting. querySelector returns the first matching element or null; querySelectorAll returns a live NodeList (actually static in modern engines) of all matches.

// Select first element with class "alert"
const firstAlert = document.querySelector('.alert');
// Select all buttons inside a form
const allButtons = form.querySelectorAll('button');
// Match element that exists in the document
if (element.matches('.expanded')) { /* … */ }

Exact error text when selector is invalid:

SyntaxError: Failed to execute 'querySelector' on 'Document': '.class #id' is not a valid selector.
TypeError: Cannot read properties of null (reading 'addEventListener')

The first occurs if the selector string itself is malformed (unbalanced brackets, unescaped pseudo-class characters). The second means querySelector returned null and you tried to chain methods on it.

Sharp edges:

Using Selectors in Testing Frameworks

Cypress

Cypress wraps querySelectorAll internally. The primary command is cy.get(), which accepts a CSS selector string.

// First matching element
cy.get('.submit-button').click();

// All matches
cy.getAll('tr').each($row => { /* … */ });

// Failing assertion with exact message
await expect(cy.get('.missing')).to.exist
// => AssertionError: Timed out retrying after 4000ms: Expected dom element to exist, but never found.

Flags & timeouts: cy.get() accepts a second options object with timeout (default 16000ms). cy.contains('Submit') searches text content, not a pure CSS selector, but is often used alongside them.

Sharp edges in production:

Playwright

Playwright uses page.locator(selector) which returns a Locator object. It is lazily evaluated and more resilient to stale element references.

const submit = page.locator('.submit-button');
await submit.click();

// Exact assertion failure text
await expect(submit).toBeVisible()
// => AssertionError: expectedLocator.toBeVisible() failed: expected element to be visible but it is not.

Flags & configuration: page.locator(selector, { timeout: 5000 }) sets per‑locator timeout. expect(locator).toHaveCount(1) verifies exact match count.

Sharp edges:

Selenium WebDriver

Selenium’s find_element and find_elements map directly to CSS selector evaluation via the browser’s native engine.

WebElement button = driver.findElement(By.cssSelector(".submit-button"));
List<WebElement> rows = driver.findElements(By.cssSelector("tr.data-row"));

Exact error text:

org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"css selector": ".submit-button"}
Build info version: 4.15.0

In Python: selenium.common.exceptions.NoSuchElementException: Message: no such element: {"css selector": "…"}

Sharp edges:

CSS Selector Syntax & Common Trap Patterns

Tag, Class, ID, and Attribute Combinations

A selector’s power comes from combination, but order and spacing matter.

/* Valid: descendant */
div.form input[type="text"] { /* … */ }

/* Valid: direct child */
ul > li.active { /* … */ }

/* Valid: adjacent sibling */
h1 + p { /* … */ }

/* Common pitfall: spacing changes meaning */
div .active   /* any .active inside div */
div.active    /* element with both div and active classes */

Exact error when selector is malformed in a stylesheet:

CSSParserError: Unexpected identifier at line 3, column 12

Browsers will stop parsing the rule set entirely; subsequent rules are unaffected unless wrapped in @media or similar.

Pseudo‑Classes That Often Confuse

SelectorWhat it actually matchesCommon mismatch
:nth-child(2n+1)Odd‑indexed children *among all siblings*Expecting odd‑indexed elements of a specific type
:nth-of-type(2n+1)Odd‑indexed children *of the same element type*Using this when siblings are mixed tags
:not(.active)Every element except those with class activeThinking it excludes descendants only, not all descendants
:first-childElement that is the first child of its parentExpecting it to match the first .item in a list

Concrete gotcha:

/* Matches the first <li> regardless of parent */
li:first-child { color: red; }

/* Does NOT match if the first child is a <span> */
ul > li:first-child { color: red; }

If your <ul> starts with <span>, the second rule matches nothing.

Specificity and the Cascade Traps

Specificity is calculated as (a, b, c, d) where a = inline styles, b = IDs, c = classes/attributes/pseudo-classes, d = element/tag names. A rule with specificity (0,1,0,0) beats (0,0,1,0).

Production breakage pattern: You add .btn { background: blue; } ( specificity 0,0,1,1 ) to override a button style. Later, a third‑party script injects <button id="action-btn"> and the existing rule #action-btn { … } (0,1,0,0) wins, but your .btn does not cascade down because the ID selector is more specific. The silent failure: button appears unstyled or inherits a default you didn’t intend.

Fix: Either increase your selector’s ID count (avoid if possible) or use !important sparingly, or restructure to target the shared parent and descend: .form .btn { … }.

Browser & Developer Tools

Chrome and Firefox expose a jQuery‑style $ and $$ in the DevTools console, but these are convenience wrappers around querySelector and querySelectorAll.

// First match
$('.alert-error');
// All matches
$$('script[type="application/json"]');
// Shorthand for complex selectors
$$('[data-tooltip]:not([data-state="disabled"])');

Exact console output when selector is invalid:

VM123:1 Uncaught SyntaxError: Failed to parse selector ".[data-state="

The console will not evaluate the selector and will print the error above, leaving the variable undefined.

Invalid Selector Error in Elements Panel

When you type a selector into the Elements panel “Search” field (⌘+F / Ctrl+F), the browser highlights matches or reports:

No matching elements found

If the syntax is broken (e.g., unclosed bracket), the search field turns red and shows:

Invalid selector: "div[data-id

Sharp Edges: What Silently Fails in Production

Empty NodeLists and Null Assumptions

The most frequent production incident involving selectors is assuming querySelectorAll returned something because the selector “looks right.” A selector like #sidebar .nav-link returns [] if the markup changed from <div id="sidebar"> to `<

This page was rewritten on 10 October 2026. It replaced a templated version that was not specific to its own title. How these pages are written and checked. If anything here is wrong, tell us at [email protected] and we will correct it.