Types of Selectors in CSS3 {HTML(5)/JavaScript}
Basic Selectors
p selects all paragraphs).class attribute, denoted by a period (.) followed by the class name (e.g., .intro).id attribute, denoted by a hash (#) followed by the ID name (e.g., #main-header). An ID should be unique within the HTML document.*).h1, h2, p applies styles to all <h1>, <h2>, and <p> elements). Combinator Selectors
Combinators define the relationship between selectors for more specific targeting based on document structure.
div p selects all <p> elements inside a <div>).>) (e.g., ul > li selects only <li> elements that are direct children of a <ul>).+) (e.g., h2 + p selects the first <p> element immediately following an <h2>).~) (e.g., h2 ~ p selects all <p> elements that follow an <h2>, regardless of the immediate order). Attribute Selectors
These selectors target elements based on the presence or value of their attributes.
[attribute]: Selects all elements with the specified attribute (e.g., a[target]).[attribute=value]: Selects all elements with the specified attribute and exact value (e.g., a[target="_blank"]).[attribute~=value]: Selects elements with an attribute value containing a specific, space-separated word (e.g., [title~="flower"]).[attribute^=value]: Selects elements whose attribute value begins with a specific value (e.g., [href^="https"]).[attribute$=value]: Selects elements whose attribute value ends with a specific value (e.g., [href$=".pdf"]).[attribute*=value]: Selects elements whose attribute value contains a specific substring (e.g., [href*="w3schools"]). Pseudo-classes and Pseudo-elements
:). Examples include:
:hover: Styles an element when the user's mouse cursor hovers over it.:focus: Styles an element when it has focus.:nth-child(): Selects elements based on their position among siblings.:not(): Selects every element that is not the specified element/selector.::). Examples include:
::before: Inserts content before the actual content of an element.::after: Inserts content after the actual content of an element.::first-line: Styles the first line of a block element.::first-letter: Styles the first letter of a block-level element.::selection: Styles the portion of an element that is selected by a user.
Selecting HTML elements in CSS3 involves various patterns called selectors. These range from basic identifiers to complex patterns that target elements based on their state or relationship to others.
Basic Selectors
These are the foundational selectors used for general targeting.
*): Selects every single element on the page.p, h1, div)..className): Selects all elements with a specific class attribute. Classes are reusable across multiple elements.#idName): Selects a single unique element with a specific id attribute. An ID should only be used once per page. Combinator Selectors
These select elements based on their specific relationships in the HTML document tree.
div p).>): Selects only the direct children of a parent element.+): Selects an element that is the immediate next sibling of a specified element.~): Selects all siblings that follow a specified element, even if they aren't immediately next to it. Attribute Selectors
CSS3 significantly expanded these, allowing you to target elements based on the presence or value of their attributes.
[attr]): Selects elements that have the specified attribute.[attr="val"]): Selects elements where the attribute exactly matches the value.[attr^="val"]): Selects elements whose attribute value starts with "val".[attr$="val"]): Selects elements whose attribute value ends with "val".[attr*="val"]): Selects elements whose attribute value contains the substring "val". Pseudo-Classes
Pseudo-classes target elements in a specific state or position without adding extra HTML.
:hover (mouse over), :active (being clicked), and :focus (keyboard focus).:first-child, :last-child, :nth-child(n), and :only-child.:enabled, :disabled, :checked, and :required (used mostly for forms).:not(selector) selects elements that do not match the given selector. Pseudo-Elements
These style specific parts of an element rather than the whole thing.
::before / ::after: Inserts content before or after the element’s actual content.::first-letter / ::first-line: Targets the very first letter or first line of a block of text.::selection: Styles the portion of text that a user has highlighted. Selector Grouping
To apply the same styles to multiple different selectors, you can group them by separating them with a comma.
h1, h2, p { color: blue; }For an exhaustive list of all CSS selectors, you can refer to the MDN Selectors Reference.
Pseudo-Classes (:)
Used to define a special state of an element, such as when a user interacts with it.
:hover (mouse over), :active (being clicked), :focus (keyboard focus).:first-child, :last-child, :nth-child(n), :only-child.:enabled, :disabled, :checked, :required, :invalid.Pseudo-Classes (Based on State/Position)
:link, :visited, :hover, :active, :focus.:first-child, :last-child, :only-child.:nth-child(n), :nth-last-child(n): Matches based on formula or keyword (even, odd).:first-of-type, :last-of-type, :nth-of-type(n).:not(selector): Selects elements that do not match the specified selector.:target: Styles the element with an ID matching the current URL fragment. In the world of web development, selectors are the bridge between your HTML structure and your CSS styling. They tell the browser exactly which elements should receive specific styles.
Think of it like this: if your HTML is the building, selectors are the addresses used to deliver the paint and furniture.
These are the bread and butter of CSS, used for targeting broad categories or specific individual elements.
Universal Selector (*): Targets every single element on the page. Usually used for "resets" (e.g., * { margin: 0; }).
Type (Element) Selector: Targets elements by their tag name (e.g., h1, p, div).
Class Selector (.): Targets elements with a specific class attribute. You can use this on multiple elements.
ID Selector (#): Targets a unique element. Each ID should only be used once per page.
These define the relationship between different elements in your HTML tree.
| Selector | Name | Description | Example |
div p |
Descendant | Any <p> inside a <div> (even if nested deep). |
|
div > p |
Child | Only <p> elements that are direct children of a <div>. |
|
div + p |
Adjacent Sibling | The very next <p> element immediately after a <div>. |
|
div ~ p |
General Sibling | Any <p> element that follows a <div> as a sibling. |
These target elements based on the presence or value of their attributes (like href, type, or src).
[target]: Selects all elements with a target attribute.
input[type="text"]: Selects only text input fields.
a[href^="https"]: Selects links where the URL starts with "https".
a[href$=".pdf"]: Selects links that end with ".pdf".
These are "state-based" or "part-based" selectors.
:)Target an element’s state.
:hover: When the mouse is over an element.
:nth-child(n): Targets the $n^{th}$ element in a group (e.g., li:nth-child(2)).
:not(selector): Targets elements that do not match the list.
::)Target a specific part of an element.
::before / ::after: Inserts content before or after an element’s content.
::first-letter: Styles the first letter of a block of text.
In JS, we use these same CSS selector strings to "grab" elements from the DOM to make them interactive.
// Grabbing a single element
const submitBtn = document.querySelector('#submit-btn');
// Grabbing a collection (List) of elements
const allCards = document.querySelectorAll('.card');
// Changing style via JS
submitBtn.style.backgroundColor = 'blue';
In the world of web development, selectors are the bridge between your HTML structure and your CSS styling. They tell the browser exactly which elements should receive specific styles.
Think of it like this: if your HTML is the building, selectors are the addresses used to deliver the paint and furniture.
When multiple CSS rules target the same element, the browser uses a scoring system to decide which style wins.
| Selector Type | Point Value | Example |
| Inline Styles | 1000 | <h1 style="color: blue;"> |
| IDs | 100 | #header |
| Classes, Attributes, Pseudo-classes | 10 | .menu, [type="text"], :hover |
| Elements & Pseudo-elements | 1 | div, p, ::before |
Pro Tip: The Universal Selector (
*) and combinators (like+or>) have a specificity of 0. If you use!important, it overrides everything, but use it sparingly—it’s the "sledgehammer" of CSS and can make debugging a nightmare.
CSS3 introduced powerful ways to target elements based on their position in the HTML without needing to add extra classes.
:first-of-type / :last-of-type: Unlike first-child, these target the first or last instance of a specific tag within a parent.
:only-child: Targets an element only if it is the solitary resident of its parent container.
:nth-last-child(n): Same as nth-child, but starts counting from the bottom up.
:empty: Targets elements that have no children and no text content (great for hiding empty alert boxes).
These are specifically used for interactive elements like forms and buttons.
:enabled / :disabled: Targets form inputs based on whether the user can interact with them.
:checked: Targets radio buttons or checkboxes that are currently selected.
:valid / :invalid: Targets inputs based on whether they meet HTML5 validation rules (like a correct email format).
:focus: Targets an element when it has been clicked or tabbed into.
JavaScript leverages the CSS Selector Engine (specifically the Sizzle engine in older libraries or native querySelectorAll) to manipulate the DOM.
getElementById('id'): The fastest way to grab a single element.
getElementsByClassName('class'): Returns a "Live" HTMLCollection.
querySelector('.class #id'): Uses CSS syntax. Returns the first match.
querySelectorAll('div > p'): Returns a static NodeList of all matches.
// Target all buttons inside a specific container
const buttons = document.querySelectorAll('.toolbar > button');
buttons.forEach(btn => {
btn.addEventListener('click', () => {
// Adding a class that triggers a CSS selector
btn.classList.toggle('is-active');
});
});
If you want multiple elements to share the same style, you don't need to write separate rules. You can group them using a comma.
h1, h2, .main-title, #hero-heading {
font-family: 'Helvetica', sans-serif;
color: #222;
}
| Goal | Best Selector |
| Single unique item | ID (#) |
| Repeating styles | Class (.) |
| Parent-to-Child flow | Combinator (>) |
| Dynamic states (hover/click) | Pseudo-class (:) |
| Content-specific (PDF links) | Attribute ([attr]) |
To round out your knowledge, we need to cover the advanced CSS3 functional selectors and the Logic-based selectors that have recently changed how we write "smart" CSS. These allow the CSS to react to the presence of other elements without needing JavaScript.
These are some of the most powerful additions to CSS3 and the subsequent specifications (CSS4/Selectors Level 4).
:is() (formerly :any): Allows you to write more compact code by grouping selectors.
Instead of: header p, main p, footer p
Use: :is(header, main, footer) p
:where(): Exactly like :is(), but it has zero specificity. This is helpful when you want to create "default" styles that are very easy for other classes to override.
:has() (The "Parent Selector"): For years, CSS couldn't look "up" the tree. Now it can.
Example: card:has(img) will only style the card if there is an image inside it.
:not() (Negation): Excludes elements from a style.
Example: li:not(.active) styles all list items except the one with the "active" class.
These target specific pieces of content that aren't wrapped in their own HTML tags.
::selection: Styles the part of the document that a user highlights with their mouse (e.g., changing the background color of highlighted text).
::placeholder: Styles the hint text inside an <input> or <textarea>.
::marker: Styles the bullet point of a list item or the number in an ordered list.
::first-line: Styles only the first line of text in a block, which is responsive (it changes if the window is resized and text wraps differently).
HTML5 brought built-in validation, and CSS3 allows you to style elements based on their "truth" value.
| Selector | Description |
:required |
Targets inputs that must be filled out before submitting. |
:optional |
Targets inputs that do not have the "required" attribute. |
:in-range |
Targets <input type="number"> when the value is within min and max. |
:out-of-range |
Targets the number input if the value exceeds the set limits. |
:read-only |
Targets elements with the readonly attribute (user can't edit). |
In a JavaScript environment (like React or Vue), or even in standard HTML5, the browser reads CSS selectors from right to left.
The Right-most Selector is the "Key": In div ul li a, the browser first finds every <a> tag on the page, then checks if it’s in a <li>, then a <ul>, etc.
Avoid Deep Nesting: Try not to go more than 3 levels deep (e.g., .nav .item .link). It makes the CSS harder to maintain and slightly slower to render.
Prefer Classes over Tags: Tag selectors (div, p) are very "greedy." Using a class (.main-content) is more specific and performant.
In modern JavaScript (ES6+), you can use selectors to create dynamic themes or complex UI logic.
// Example: Targeting an element using an attribute selector in JS
const themeToggle = document.querySelector('[data-action="toggle-theme"]');
// Example: Using :closest() to find a parent (The JS version of :has)
const card = event.target.closest('.card-container');
| Feature | CSS Selectors | JavaScript (querySelector) |
| Purpose | Presentation/Layout | Behavior/Logic |
| Speed | Extremely High (Native) | High (Requires API call) |
| Dynamic | Limited to states (hover/focus) | Fully dynamic (on click, scroll, etc.) |
| Scope | Global to the stylesheet | Limited to the DOM tree |
To give you the "fullest" picture possible, we have to look at Structural Pseudo-classes, Logical Combinators, and the Relationship between CSS selectors and the DOM tree.
These selectors allow you to style elements based on their exact position within a parent, which is essential for layouts like tables, grids, and galleries.
:nth-child(an + b): This uses a formula where a is a cycle size, n is a counter (starting at 0), and b is an offset value.
li:nth-child(3n) selects every 3rd item.
li:nth-child(odd) or li:nth-child(2n+1) selects all odd items.
:nth-of-type(n): Similar to nth-child, but it ignores other element types. If you have a mix of <div> and <p>, p:nth-of-type(2) will find the second paragraph specifically.
:only-of-type: Targets an element only if it is the only one of its kind within its parent.
These are the newest and most "intelligent" selectors. They act almost like programming logic within your stylesheet.
:has() (The "Parent" Selector):
This was the "holy grail" of CSS for decades. It allows you to style a parent based on what is happening inside it.
article:has(img) { border: 1px solid blue; } — This styles the article only if it contains an image.
:is() and :where():
Used to simplify long lists of selectors.
header h1, nav h1, footer h1 becomes :is(header, nav, footer) h1.
The Difference: :is() takes the specificity of its most specific argument, while :where() always has a specificity of 0.
:not():
The negation pseudo-class.
input:not([type="submit"]) styles every input field except the submit button.
When you combine selectors, the browser calculates a "weight." If two rules conflict, the higher weight wins.
Think of it as a four-digit score: (Inline, ID, Class/Attribute, Element).
| Selector | Score | Why? |
h1 |
0, 0, 0, 1 | 1 Element |
.container h1 |
0, 0, 1, 1 | 1 Class + 1 Element |
#nav .link:hover |
0, 1, 2, 0 | 1 ID + 1 Class + 1 Pseudo-class |
div#main .content p |
0, 1, 1, 2 | 1 ID + 1 Class + 2 Elements |
In JavaScript, selectors are used to "query" the DOM. While CSS uses them for styling, JS uses them for functionality.
document.querySelector(): Returns the first element that matches a CSS selector.
document.querySelectorAll(): Returns a NodeList (all matches).
element.matches('.some-class'): Returns true or false if an element matches a selector.
element.closest('.container'): Traverses up the DOM tree to find the nearest ancestor matching the selector.
// Example: Selecting all 'active' links inside a navigation bar
const activeLinks = document.querySelectorAll('nav a.active');
activeLinks.forEach(link => {
console.log(link.href);
});
If you are working with Web Components (HTML5 customElements), you encounter Shadow DOM selectors:
:host: Selects the custom element itself from within its own shadow root.
:host-context(.dark-theme): Selects the element only if it (or any ancestor) has the .dark-theme class.
::slotted(span): Styles elements that are passed into a "slot" from the light DOM.
| Category | Selectors |
| Basic | *, element, .class, #id |
| Combinators | space, >, +, ~ |
| Attributes | [attr], [attr="val"], [attr^="val"], [attr$="val"] |
| Pseudo-Classes | :hover, :focus, :nth-child, :valid, :has, :not |
| Pseudo-Elements | ::before, ::after, ::first-letter, ::placeholder |
To reach the absolute "edge" of CSS3 and modern selection logic, we need to discuss Responsive Selectors, Mathematical functions in selection, and how JavaScript Performance is impacted by the way you write your selectors.
Beyond simple hovers, CSS can detect specific user interactions and device states without a single line of JavaScript.
:focus-within: This styles an element if it—or any of its children—has focus.
Use Case: Highlighting an entire form row when the user clicks a specific input field inside it.
:focus-visible: Styles an element only when the browser determines that focus should be visible (e.g., when a user navigates via keyboard but not via mouse).
:target: Selects an element whose id matches the current URL fragment (the part after the #).
Use Case: Creating "pure CSS" lightboxes or tab systems.
:active: Selects an element during the exact moment it is being activated (clicked or pressed).
CSS allows for complex logic when targeting ranges of elements.
Complex nth-child: You can use the of selector syntax to filter before counting.
li:nth-child(2 of .important): Selects the second item that also has the class .important, ignoring other li items.
:nth-last-of-type(n): Starts the count from the last sibling of that type.
The Range Selectors:
input:in-range: Targets inputs (like number or range) where the value is within the min and max attributes.
input:out-of-range: Useful for showing instant error styling.
When you use JavaScript to find elements, the browser handles different selectors with varying levels of efficiency.
getElementById(): Fastest. Direct map lookup in the browser’s internal table.
getElementsByTagName(): Very fast. Returns a "Live Collection" (updates automatically if the DOM changes).
querySelectorAll(): Slower but most flexible. It parses the string as CSS and returns a "Static NodeList" (does not update if the DOM changes).
While CSS has :has(), JavaScript has used .closest() for years.
// Find the nearest parent '.card' starting from the clicked button
const parentCard = event.target.closest('.card');
CSS3 selectors aren't just about "what" the element is, but "where" and "how" it's being viewed.
@media Selectors: Technically "at-rules," but they act as global selectors for device states.
(orientation: landscape)
(prefers-color-scheme: dark): Allows you to select and style elements based on the user's OS settings.
(hover: hover): Selects devices that actually have a mouse/pointer (prevents hover styles from "sticking" on touchscreens).
| Selector | Logic Type | Real-World Application |
input:indeterminate |
State | Styling "Select All" checkboxes that are only partially checked. |
::selection |
UI | Customizing the highlight color when a user drags their mouse over text. |
:dir(rtl) |
Direction | Changing layout for Right-to-Left languages (Arabic/Hebrew). |
:fullscreen |
Display | Styling an element only when it is in browser full-screen mode. |
::backdrop |
Layering | Styling the background behind a <dialog> or full-screen element. |
Avoid "Over-qualifying" Selectors. Writing div.container ul.list li.item a.link is redundant. Because CSS is read Right-to-Left, the browser has to do five checks for every link. Simply writing .nav-link is significantly faster and easier to read.
Would you like me to provide a JavaScript code block that demonstrates how to create a "Live Search" filter using these CSS attribute selectors dynamically?