Types of Selectors
Browser DOM methods like querySelector() and querySelectorAll() use CSS selectors.
If you understand selector types, you can find elements accurately and avoid brittle DOM code.
Type Selectors
A type selector matches elements by tag name.
const firstParagraph = document.querySelector("p");
const allButtons = document.querySelectorAll("button");HTML:
<p>First paragraph</p>
<p>Second paragraph</p>
<button>Save</button>Type selectors are simple, but they can be too broad on large pages.
ID Selectors
An ID selector starts with #.
<h1 id="page-title">Settings</h1>const title = document.querySelector("#page-title");IDs should be unique in a document.
Use IDs for important single elements, but avoid creating too many global dependencies around them.
Class Selectors
A class selector starts with ..
<button class="primary">Save</button>
<button class="secondary">Cancel</button>const primaryButton = document.querySelector(".primary");
const buttons = document.querySelectorAll(".primary, .secondary");Classes are useful because multiple elements can share them.
They are often better than tag selectors when selecting repeated UI pieces.
Attribute Selectors
Attribute selectors match elements with specific attributes.
<input name="email" type="email">
<input name="password" type="password">const email = document.querySelector('[name="email"]');
const passwordFields = document.querySelectorAll('input[type="password"]');Attribute selectors are useful for forms and data-* attributes.
<button data-action="save">Save</button>
<button data-action="delete">Delete</button>const saveButton = document.querySelector('[data-action="save"]');Descendant Selectors
A descendant selector matches elements inside other elements.
<nav>
<a href="/home">Home</a>
</nav>
<main>
<a href="/help">Help</a>
</main>const navLinks = document.querySelectorAll("nav a");This selects only links inside nav.
Child Selectors
A child selector uses >.
It matches direct children only.
<ul class="menu">
<li>Home</li>
<li>
Products
<ul>
<li>Books</li>
</ul>
</li>
</ul>const topLevelItems = document.querySelectorAll(".menu > li");This selects only the direct li children of .menu.
Combining Selectors
Selectors can be combined.
<button class="primary" data-action="save">Save</button>const saveButton = document.querySelector('button.primary[data-action="save"]');This means:
- a
button - with class
primary - with
data-action="save"
Combined selectors can be useful, but do not make them more complicated than needed.
Multiple Selectors
Use commas to match more than one selector.
const controls = document.querySelectorAll("button, input, select, textarea");This selects all listed element types.
Pseudo-Classes
Some CSS pseudo-classes work in selectors.
const checked = document.querySelectorAll("input:checked");
const disabledButtons = document.querySelectorAll("button:disabled");
const firstItem = document.querySelector("li:first-child");These can be helpful for form state.
Not every CSS pseudo-class is useful or appropriate for JavaScript selection, but many common ones work.
Escaping Special Characters
Selectors have syntax rules.
If an ID or class contains special characters, the selector may need escaping.
<div id="user:42">Asha</div>This selector can fail:
document.querySelector("#user:42");Use CSS.escape() when building selectors from dynamic values.
const id = "user:42";
const element = document.querySelector(`#${CSS.escape(id)}`);Avoid building selectors from user input unless you understand the escaping requirements.
Selector Specificity vs DOM Selection
CSS specificity decides which style rule wins.
DOM selection does not use specificity to decide the "best" match.
querySelector() returns the first matching element in document order.
const element = document.querySelector(".card .title");If several elements match, only the first one is returned.
Use querySelectorAll() when you expect multiple matches.
Choosing Good Selectors
Good JavaScript selectors are:
- stable
- readable
- scoped
- tied to behavior, not fragile styling details
This can be brittle:
document.querySelector("main > div > div > button");Small HTML changes can break it.
This is clearer:
<button data-action="save-profile">Save</button>document.querySelector('[data-action="save-profile"]');For larger projects, teams often use data-* attributes specifically for JavaScript hooks or tests.
Best Practices
- Use class, ID, or
data-*selectors for meaningful targets. - Search inside a known container to avoid accidental matches.
- Avoid very long structure-based selectors.
- Use
CSS.escape()for dynamic selector parts. - Use
querySelectorAll()when multiple elements should match.
Common Mistakes
Forgetting the # for an ID:
document.querySelector("submit-button"); // looks for a <submit-button> tagCorrect:
document.querySelector("#submit-button");Forgetting the . for a class:
document.querySelector("active"); // looks for an <active> tagCorrect:
document.querySelector(".active");Summary
Selectors connect your JavaScript to specific parts of the DOM.
Use simple, stable selectors when possible. Understand type, ID, class, attribute, descendant, child, combined, and pseudo-class selectors so your DOM code selects exactly what it means to select.