Introduction to Advanced Selectors
In our previous lectures, we covered basic CSS selectors: element, class, and ID selectors. While these are the foundation of CSS selection, the true power of CSS emerges when you master more advanced selectors.
Advanced selectors allow for more precise targeting of elements based on their attributes, states, positions, and relationships. This precision reduces the need for extra markup and classes, leading to cleaner HTML and more maintainable CSS.
"Specificity is to CSS what location is to real estate - it's all about targeting the right element in the right context."
Think of advanced selectors as sophisticated filters that help you target elements with surgical precision, rather than using broad, generic selectors that might catch unintended elements.
Attribute Selectors
Attribute selectors target elements based on the presence or value of their HTML attributes. They provide a powerful way to style elements without adding extra classes.
Basic Attribute Selector [attr]
The most basic attribute selector targets elements that have a specific attribute, regardless of its value.
/* Targets any element with a title attribute */
[title] {
cursor: help;
}
/* Targets any input with a required attribute */
input[required] {
border-color: red;
}
/* Targets any element with a data-tooltip attribute */
[data-tooltip] {
position: relative;
}
HTML Example
<input type="text" required>
<input type="text">
<a href="#" title="Click for more information">More info</a>
<button data-tooltip="Click to save">Save</button>
Real-World Use Cases
- Styling required form inputs:
input[required] - Adding help cursors to elements with tooltips:
[title] - Styling elements with custom data attributes:
[data-state] - Targeting disabled elements:
[disabled]
Exact Value Attribute Selector [attr="value"]
This selector targets elements where the attribute has an exact value match.
/* Targets input elements with type="text" */
input[type="text"] {
padding: 5px;
border: 1px solid #ccc;
}
/* Targets links with target="_blank" */
a[target="_blank"] {
padding-right: 20px;
background: url('external-link-icon.svg') no-repeat right;
}
/* Targets elements with a specific ARIA role */
[role="navigation"] {
background-color: #f8f8f8;
}
HTML Example
<input type="text" placeholder="Enter name">
<input type="email" placeholder="Enter email">
<a href="https://example.com" target="_blank">External Link</a>
<nav role="navigation">...</nav>
Real-World Use Cases
- Styling specific input types:
input[type="checkbox"] - Adding icons to links that open in new tabs:
a[target="_blank"] - Targeting elements by their ARIA roles:
[role="tab"] - Styling language-specific elements:
[lang="fr"]
Value Contains Attribute Selector [attr*="value"]
This selector targets elements where the attribute value contains the specified substring anywhere.
/* Targets links whose href contains "example" anywhere */
a[href*="example"] {
color: purple;
}
/* Targets elements with class containing "btn" anywhere */
[class*="btn"] {
cursor: pointer;
}
/* Targets elements with data-id containing specific numbers */
[data-id*="47"] {
background-color: yellow;
}
Caution
This is the most permissive attribute value selector. It will match even if the value is part of another word, which can sometimes lead to unintended matches.
Real-World Use Cases
- Styling links to specific domains:
a[href*="youtube.com"] - Finding elements with partial class names:
[class*="success"] - Targeting file types in download links:
a[href*=".pdf"]
Value Starts With Attribute Selector [attr^="value"]
This selector targets elements where the attribute value begins with the specified value.
/* Targets links whose href starts with "https" */
a[href^="https"] {
color: green;
}
/* Targets elements with class starting with "icon-" */
[class^="icon-"] {
display: inline-block;
width: 16px;
height: 16px;
}
/* Targets all headings with data-section starting with "intro" */
h1[data-section^="intro"], h2[data-section^="intro"] {
color: blue;
}
HTML Example
<a href="https://secure-site.com">Secure Site</a>
<a href="http://regular-site.com">Regular Site</a>
<span class="icon-home"></span>
<span class="icon-search"></span>
<h2 data-section="intro-main">Introduction</h2>
Real-World Use Cases
- Distinguishing between HTTP and HTTPS links:
a[href^="https"] - Styling icon systems:
[class^="icon-"] - Targeting file types:
a[href^="mailto:"] - Categorizing sections by prefix:
[id^="section-"]
Value Ends With Attribute Selector [attr$="value"]
This selector targets elements where the attribute value ends with the specified value.
/* Targets links to specific file types */
a[href$=".pdf"] {
background-image: url('pdf-icon.svg');
padding-left: 20px;
}
a[href$=".doc"], a[href$=".docx"] {
background-image: url('word-icon.svg');
padding-left: 20px;
}
/* Targets input fields with specific naming conventions */
input[name$="-name"] {
text-transform: capitalize;
}
HTML Example
<a href="document.pdf">Download PDF</a>
<a href="report.docx">Download Word Document</a>
<a href="spreadsheet.xlsx">Download Excel File</a>
<input type="text" name="first-name">
<input type="text" name="last-name">
Real-World Use Cases
- Adding file type icons to download links:
a[href$=".zip"] - Styling inputs by their name pattern:
input[name$="date"] - Targeting elements with specific IDs:
[id$="-container"]
Space-Separated Value Attribute Selector [attr~="value"]
This selector targets elements where the attribute value contains the specified word as a whole, space-separated word.
/* Targets elements with a specific class among multiple classes */
[class~="primary"] {
font-weight: bold;
}
/* Targets elements with rel attribute containing "nofollow" as a whole word */
a[rel~="nofollow"] {
color: #999;
}
/* Targets images with specific alt text words for better accessibility */
img[alt~="diagram"] {
max-width: 100%;
border: 1px solid #ddd;
}
HTML Example
<div class="box primary container">Primary Box</div>
<div class="box secondary container">Secondary Box</div>
<a href="#" rel="nofollow external">External Link</a>
<img src="process.png" alt="workflow diagram">
Difference from [attr*="value"]
[class~="primary"] will match class="box primary container" but not class="box primary-container", while [class*="primary"] would match both.
Real-World Use Cases
- Targeting specific classes without using class selectors:
[class~="active"] - Styling links with specific relationship types:
a[rel~="nofollow"] - Enhancing a11y by targeting images with specific alt text:
img[alt~="icon"]
Hyphen-Separated Value Attribute Selector [attr|="value"]
This selector targets elements where the attribute value is exactly the specified value or begins with the specified value followed by a hyphen.
/* Targets elements with lang attribute set to "en" or starting with "en-" */
[lang|="en"] {
font-family: 'Arial', sans-serif;
}
/* Targets data-region values for a group of related regions */
[data-region|="north"] {
background-color: #f0f8ff;
}
/* Targets classes using BEM-like naming conventions */
[class|="block"] {
padding: 1rem;
}
HTML Example
<p lang="en">English text</p>
<p lang="en-US">American English text</p>
<p lang="en-GB">British English text</p>
<p lang="fr">French text</p>
<div data-region="north">North</div>
<div data-region="north-east">North East</div>
<div class="block">Block</div>
<div class="block-element">Block Element</div>
Real-World Use Cases
- Language-specific styling:
[lang|="fr"]for French and its dialects - Regional or geographic targeting:
[data-country|="us"] - Component frameworks using hyphen separators:
[class|="card"]
Case-Sensitivity in Attribute Selectors
By default, attribute selectors are case-sensitive. CSS Selectors Level 4 introduced the i modifier to perform case-insensitive matching.
/* Case-sensitive matching (default) */
[type="text"] { /* Matches type="text" but not type="TEXT" */ }
/* Case-insensitive matching with the "i" modifier */
[type="text" i] { /* Matches type="text", type="TEXT", type="Text", etc. */ }
[lang|="en" i] { /* Case-insensitive language matching */ }
Browser Support Note
Case-insensitive attribute selectors are well-supported in modern browsers but may not work in older browsers. Always check compatibility if supporting older browsers is required.
Combining Attribute Selectors
You can combine attribute selectors with other selectors or use multiple attribute selectors together for more precise targeting.
/* Combining with element selectors */
input[type="text"][required] {
border: 2px solid red;
}
/* Combining with class selectors */
.form-control[disabled] {
background-color: #f5f5f5;
cursor: not-allowed;
}
/* Multiple attribute conditions */
a[href^="https"][target="_blank"] {
background-image: url('secure-external-link.svg');
background-position: right;
background-repeat: no-repeat;
padding-right: 20px;
}
HTML Example
<input type="text" required placeholder="Required Field">
<input type="text" placeholder="Optional Field">
<input class="form-control" type="text" disabled>
<a href="https://example.com" target="_blank">Secure External Link</a>
Common Combinations
- Targeting specific form elements:
input[type="checkbox"][checked] - Styling validated fields:
input[type="email"][data-valid="true"] - External secure links:
a[href^="https:"][target="_blank"] - Component variations:
.btn[data-size="large"][data-theme="dark"]
Pseudo-class Selectors
Pseudo-class selectors target elements based on their state, position, or relationship that can't be expressed using simple selectors. They begin with a colon (:).
User Interaction Pseudo-classes
These pseudo-classes respond to user interactions with elements.
/* Changes button appearance when hovered */
button:hover {
background-color: #0056b3;
color: white;
}
/* Styles while an element is being activated (clicked/tapped) */
button:active {
transform: translateY(2px);
box-shadow: none;
}
/* Styles when an element has focus (keyboard navigation) */
input:focus {
outline: 2px solid #0066cc;
box-shadow: 0 0 5px rgba(0, 102, 204, 0.5);
}
/* Modern focus styles that only apply when using keyboard */
button:focus-visible {
outline: 3px solid orange;
}
/* Styling for when user is actively touching element on touch devices */
button:hover:active {
background-color: #003366;
}
Note: Hover the button to see color change, click to see active state, and focus the input to see focus styling.
Accessibility Best Practices
- Always provide visible focus styles for keyboard navigation
- Consider using
:focus-visibleto differentiate between mouse and keyboard focus - Don't rely solely on hover for important features (mobile devices don't have hover)
- Ensure active states have sufficient contrast
Form State Pseudo-classes
These pseudo-classes target form elements based on their state or user interaction.
/* Required form fields */
input:required {
border-left: 4px solid #cc0000;
}
/* Valid and invalid form inputs */
input:valid {
border-color: green;
}
input:invalid {
border-color: red;
}
/* Checked radio buttons and checkboxes */
input:checked + label {
font-weight: bold;
}
/* Disabled form elements */
input:disabled {
background-color: #f1f1f1;
cursor: not-allowed;
}
/* Optional inputs */
input:optional {
border-left: 4px solid #cccccc;
}
/* When an element is in focus and valid/invalid */
input:focus:valid {
box-shadow: 0 0 5px rgba(0, 255, 0, 0.5);
}
input:focus:invalid {
box-shadow: 0 0 5px rgba(255, 0, 0, 0.5);
}
Real-World Use Cases
- Form validation feedback:
input:invalid,input:valid - Custom checkbox/radio styling:
input:checked - Visual cues for required fields:
input:required - Disabling interaction with unusable elements:
button:disabled
Link State Pseudo-classes
These pseudo-classes target links based on their visited status and user interaction.
/* Unvisited links */
a:link {
color: #0066cc;
text-decoration: none;
}
/* Visited links */
a:visited {
color: #551a8b;
}
/* Link hover state */
a:hover {
text-decoration: underline;
}
/* Link active state (when being clicked) */
a:active {
color: #ff0000;
}
/* Link that has focus via keyboard navigation */
a:focus {
outline: 2px dashed #0066cc;
outline-offset: 2px;
}
Order Matters: LVHA
For link pseudo-classes to work correctly, they should generally be defined in this order: :link, :visited, :hover, :active (often remembered with the mnemonic "LoVe HAte").
Privacy Restrictions on :visited
For security and privacy reasons, browsers limit which CSS properties can be used with :visited. Generally, only color-related properties work, and you cannot use getComputedStyle() to detect visited status via JavaScript.
Structural Pseudo-classes
These pseudo-classes select elements based on their position in the document tree or relationship to other elements.
/* First child of its parent */
li:first-child {
font-weight: bold;
}
/* Last child of its parent */
li:last-child {
border-bottom: none;
}
/* Every odd-numbered child */
tr:nth-child(odd) {
background-color: #f5f5f5;
}
/* Every even-numbered child */
tr:nth-child(even) {
background-color: #ffffff;
}
/* Every third child */
li:nth-child(3n) {
color: red;
}
/* Second child */
li:nth-child(2) {
background-color: yellow;
}
/* First element of a specific type */
p:first-of-type {
font-size: 1.2em;
}
/* Last element of a specific type */
p:last-of-type {
margin-bottom: 2em;
}
/* Every other element of a specific type */
img:nth-of-type(2n) {
float: right;
}
/* Elements with no children (including text nodes) */
div:empty {
display: none;
}
HTML Example
<ul>
<li>First item (first-child)</li>
<li>Second item (nth-child(2))</li>
<li>Third item (nth-child(3))</li>
<li>Fourth item</li>
<li>Fifth item (last-child)</li>
</ul>
<div>
<h2>Heading (first-child, first-of-type)</h2>
<p>First paragraph (first-of-type)</p>
<p>Second paragraph</p>
<p>Third paragraph (last-of-type)</p>
</div>
<div></div> <!-- This is an empty div -->
| Odd row |
| Even row |
| Odd row |
| Even row |
- First item (first-child)
- Second item (nth-child(2))
- Third item (nth-child(3n))
- Fourth item
- Fifth item (last-child, no border)
:nth-child vs :nth-of-type
:nth-child(n) counts all children, regardless of type.
:nth-of-type(n) counts only elements of the same type.
<div>
<h2>Heading</h2>
<p>First paragraph</p> <!-- This is p:nth-child(2) and p:nth-of-type(1) -->
<p>Second paragraph</p> <!-- This is p:nth-child(3) and p:nth-of-type(2) -->
</div>
Advanced nth-child Formulas
:nth-child() and :nth-of-type() accept formulas in the form an+b where:
ais the cycle sizenis a counter (0, 1, 2, ...)bis the offset
/* Every third element, starting with the first */
:nth-child(3n+1) {
background-color: lightblue;
}
/* All elements from the 5th onwards */
:nth-child(n+5) {
color: gray;
}
/* The first 3 elements */
:nth-child(-n+3) {
font-weight: bold;
}
Logical Pseudo-classes
These pseudo-classes provide logical operations to create more complex selectors.
:not() - Negation Pseudo-class
:not() selects elements that don't match the specified selector.
/* Select all inputs that are not disabled */
input:not([disabled]) {
background-color: white;
}
/* Select all list items except the first one */
li:not(:first-child) {
margin-top: 10px;
}
/* Apply border to all elements except images */
.content > *:not(img) {
border: 1px solid #ddd;
}
/* Multiple conditions (modern browsers) */
button:not(.primary):not(.secondary) {
background-color: gray;
}
:is() and :where() - Grouping Pseudo-classes
These newer pseudo-classes simplify complex selector lists.
/* Without :is() */
header h1, header h2, header h3,
footer h1, footer h2, footer h3,
main h1, main h2, main h3 {
font-family: 'Georgia', serif;
}
/* With :is() */
:is(header, footer, main) :is(h1, h2, h3) {
font-family: 'Georgia', serif;
}
/* :where() works the same way but has zero specificity */
:where(header, footer, main) :where(h1, h2, h3) {
/* Easier to override because of zero specificity */
color: blue;
}
Specificity Difference
:is() takes the specificity of its most specific argument.
:where() always has zero specificity, making its rules easy to override.
Browser Support Note
:is() and :where() are relatively new but well-supported in modern browsers. For older browsers, you might need to provide fallbacks or use a preprocessor.
Other Notable Pseudo-classes
/* Root element selector (usually ) */
:root {
--primary-color: #0066cc;
--secondary-color: #ff7700;
}
/* Target element being targeted by URL fragment */
:target {
background-color: #fffbcc;
animation: highlight 2s ease;
}
/* Match elements that have focus or contain an element that has focus */
:focus-within {
border-color: blue;
}
/* First-letter/line pseudo-elements (note the double colon) */
p::first-letter {
font-size: 1.5em;
font-weight: bold;
}
/* Only child (element that is an only child of its parent) */
li:only-child {
list-style-type: none;
}
/* Empty (element with no children, including text nodes) */
div:empty {
display: none;
}
/* Has (parent selector) - check if element has specific children */
section:has(h2) {
margin-top: 2rem;
}
a:has(> img) {
text-decoration: none;
}
Browser Support Note
:has() is a recent addition to CSS and might not be supported in all browsers. Always check compatibility when using newer selectors.
Combining Selectors for Maximum Effect
The true power of CSS comes from combining different types of selectors to create precise targeting patterns.
/* Target required text inputs within a specific form */
.contact-form input[type="text"]:required {
border-left: 4px solid red;
}
/* Style navigation links differently based on current page */
nav a[href$=".html"]:not([href$="current-page.html"]) {
font-weight: normal;
}
/* Target all table cells in odd rows and even columns */
tr:nth-child(odd) td:nth-child(even) {
background-color: #f0f0f0;
}
/* Highlight form fields with validation errors that have user interaction */
input:invalid:not(:focus):not(:placeholder-shown) {
background-color: #fff0f0;
border-color: #ff0000;
}
/* Style the active navigation item */
.nav-item:has(> a[aria-current="page"]) {
background-color: #f0f0f0;
}
Strategic Approach to Selectors
- Start with element or class selectors for broad targeting
- Refine with attribute selectors for more specific conditions
- Add pseudo-classes for state and position-based styling
- Use logical selectors to exclude exceptions
- Consider specificity to ensure the right rules win
Performance Considerations
While advanced selectors are powerful, they can impact rendering performance if used excessively or poorly.
Selector Performance Best Practices
-
Selector Efficiency: Browsers read selectors from right to left
- Efficient:
.specific-class(direct class) - Less efficient:
div > div > div > .deep-class(complex nesting)
- Efficient:
-
Limit Selector Depth: Very deep selectors can slow down matching
- Prefer classes on key elements over deep nesting
- Use child selectors (
>) instead of descendant selectors when possible
-
Be Cautious with Universal Selectors:
*and[attribute]can be expensive- Avoid patterns like
* > * > *or[data-*]without specific values
- Avoid patterns like
-
Structural Pseudo-classes:
:nth-childand similar selectors can be expensive when used with complex selectors
Perspective on Performance
Modern browsers have optimized selector matching significantly. While it's good to be aware of performance implications, readability and maintainability should be your primary concern until performance issues are actually observed.
For most websites, smart selector usage will never be a bottleneck, and you're more likely to face performance issues from heavy JavaScript, large images, or network requests.
Practice Exercise
Advanced Selector Challenge
Create an interactive form that uses advanced selectors to provide a rich user experience without JavaScript. Your form should:
-
Use attribute selectors to:
- Style different input types differently (text, email, password, etc.)
- Add appropriate icons to input fields based on their type
- Style external links differently from internal links
-
Use pseudo-class selectors to:
- Provide clear visual feedback for form validation states (valid/invalid)
- Create a custom-styled checkbox or radio button
- Style the form differently based on required/optional fields
- Implement different hover and focus states
-
Use structural pseudo-classes to:
- Style the first and last form sections differently
- Create zebra-striping for any lists or tables
- Style groups of related form elements with patterns
-
Use logical pseudo-classes to:
- Apply styles to all form elements except a specific type
- Create a selector that targets multiple types of elements with the same style
- Implement style targeting for fields that have been interacted with but are invalid
Bonus challenge: Implement a responsive design using only CSS, where certain elements are selectively shown or hidden based on screen size (using media queries combined with advanced selectors).
Conclusion
Attribute and pseudo-class selectors greatly expand your ability to target HTML elements with precision, reducing the need for extra markup and classes. These selectors enable you to:
- Create more maintainable code by reducing redundant classes
- Build interactive interfaces without relying on JavaScript
- Target elements based on their intrinsic properties and states
- Implement complex styling patterns with cleaner HTML structure
- Improve accessibility by visually indicating state and interaction
As you develop your CSS skills, these advanced selectors will become valuable tools in your toolkit, allowing you to write more efficient, expressive, and maintainable stylesheets.
In our next lecture, we'll explore Descendant and Sibling Combinators, which will further extend your ability to target elements based on their relationships in the document tree.
Additional Resources
Interactive Learning
- CSS Diner - A fun game to learn CSS selectors
- CSS-Tricks: Selectors - Visual examples of various selectors
- Selectors Quest - Challenge-based selector exercises
Tools
- Specificity Calculator - Calculate the specificity of CSS selectors
- RegExr - For understanding pattern matching in attribute selectors
- Can I Use - Check browser support for CSS features