Selectors are one of the most fundamental concepts in front-end development. They determine which elements you target whether for styling with CSS or manipulation with JavaScript.
This guide starts with the basics and gradually moves into advanced patterns you can use in real projects, including A/B testing scripts, dynamic DOM manipulation, and powerful attribute-based selections.
1. What Are Selectors?
Selectors are patterns used to target elements in the DOM.
CSS selectors: decide what to style.
JavaScript selectors: decide what to read, change, move, clone, hide, or remove.
JavaScript uses:
const element = document.querySelector("selector");
const elements = document.querySelectorAll("selector");
2. Basic Selectors
2.1 Element Selector
/* CSS */
p { color: #333; }
// JavaScript
const paragraphs = document.querySelectorAll("p");
2.2 Class Selector
/* CSS */
.card { border-radius: 8px; }
// JavaScript
const card = document.querySelector(".card");
2.3 ID Selector
/* CSS */
#hero { height: 400px; }
// JavaScript
const hero = document.querySelector("#hero");
2.4 Universal Selector
/* CSS */
* { box-sizing: border-box; }
3. Combining Selectors
3.1 Comma Selector (Multiple)
h1, h2, h3 { font-family: sans-serif; }
3.2 Descendant Selector
nav ul li a { color: black; }
3.3 Direct Child Selector
.container > p { margin-bottom: 20px; }
3.4 Adjacent Sibling Selector
h2 + p { margin-top: -10px; }
3.5 General Sibling Selector
h2 ~ p { color: grey; }
4. Attribute Selectors
4.1 Equals
input[type="email"] { border: 2px solid blue; }
4.2 Contains (*=)
a[href*="product"] { color: green; }
4.3 Starts With (^=)
a[href^="/collections"] { font-weight: bold; }
4.4 Ends With ($=)
img[src$=".svg"] { width: 40px; }
5. CSS Pseudo-Classes
5.1 :hover
button:hover { opacity: 0.7; }
5.2 nth-child
ul li:nth-child(2) { color: red; }
5.3 :not()
.button:not(.primary) { background: #efefef; }
6. JavaScript Selector Patterns
6.1 Excluding disabled buttons
document.querySelectorAll("button:not([disabled])");
6.2 Selecting inside a container
document.querySelector(".product-card .price");
6.3 Using data attributes
document.querySelector('[data-variant="A"]');
6.4 Using closest()
element.closest(".container");
7. Advanced CSS Selectors
7.1 :has() - Parent Selector
.card:has(img) { border: 2px solid green; }
7.2 :is() - Matches Any
:is(h1, h2, h3) { color: #222; }
7.3 Conditional styles with :has()
.product-card:has(.discount) .price { color: red; }
8. Advanced JavaScript Techniques
8.1 Deep targeting
document.querySelector(".menu li:nth-child(3) a");
8.2 MutationObserver for dynamic pages
const observer = new MutationObserver(() => {
const el = document.querySelector(".dynamic-content");
if (el) console.log("Loaded!");
});
observer.observe(document.body, {
childList: true,
subtree: true
});
9. Real-World Examples
9.1 Style external links
a[href^="http"]:not([href*="yourwebsite.com"]) {
color: orange;
}
9.2 Select product cards with discount
document.querySelectorAll('.product-card:has(.discount)');
9.3 A/B Test: Move an element
const delivery = document.querySelector(".delivery-estimate");
const priceBox = document.querySelector(".price-wrapper");
if (delivery && priceBox) {
priceBox.prepend(delivery);
}
10. Handling Dynamic Classes
Modern frameworks like Tailwind, React, or CSS Modules often generate dynamic or utility-heavy class
names (e.g., .css-1a2b3c or .flex.p-4.bg-red-500). These are unreliable for
A/B testing because they can change on the next build.
The Golden Rule: Never rely on a class that looks like a random string or a long list of utility classes. Look for stable attributes instead.
10.1 Target by Text Content
If an element has unique text (like a button label), use XPath or a custom helper.
// Find button with text "Add to Cart"
const btn = Array.from(document.querySelectorAll('button'))
.find(el => el.textContent.trim() === 'Add to Cart');
10.2 Target by Attribute
Look for stable attributes like href, name, id, or
aria-label.
// Unreliable
document.querySelector('.btn-primary-123');
// Reliable
document.querySelector('a[href="/checkout"]');
document.querySelector('button[aria-label="Close menu"]');
10.3 Target by Relationship
Find a stable parent or sibling and navigate from there.
// Find the stable header, then get the first button inside it
document.querySelector('header#main-nav button');
11. Best Practices
- Prefer classes over IDs for styling
- Keep selectors short and readable
- Use data attributes for JavaScript hooks
- Avoid over-nesting (max 3 levels)
- Use modern selectors like :has() and :is()
- Test selector performance on large DOMs
- Use semantic HTML to reduce selector complexity