DEV Community

AnthonyYellowe
AnthonyYellowe

Posted on • Edited on

Exploring Advanced Selectors Like :has() and :not()

Image description

Imagine you're painting a website. Regular CSS selectors are like your paintbrushes. They let you pick which elements to style, like headings, buttons, or paragraphs. You can use them for basic jobs like making all the headings blue.

But what if you want to get more detailed? That's where fancy new brushes like :has() and :not() come in. :has() lets you target elements based on what's inside them. Think of it like a brush that only paints elements with a specific friend. :not() is like a filter brush. You can choose a group of elements, then use :not() to skip over any that don't fit your needs.

These fancy brushes are important for web developers because they make their job easier. With them, they can paint styles on exactly the right elements, making the code cleaner and easier to understand. They can also create fancy effects that wouldn't be possible with regular brushes. It's like having a whole new toolbox to make beautiful and functional websites.

Understanding :has() Selector

In this section, we will delve into the world of :has(), exploring its definition, use cases, and practical applications.

The :has() pseudo-class selects an element if it contains at least one descendant element that matches a specified selector. It takes a selector list as an argument, which can include element names, classes, IDs, or other selectors combined with various combinators.

Here's the basic syntax:

element:has(selector-list) {
  /* styles for the element */
}
Enter fullscreen mode Exit fullscreen mode

Use cases

The :has() selector unlocks a variety of possibilities for styling your web pages, here are steps to follow to when targeting an element to style.

Selecting elements based on their descendants

  • Target elements with specific child elements
figure:has(figcaption) { /* styles for figures with captions */ }
Enter fullscreen mode Exit fullscreen mode
  • Select elements based on deeper descendants
nav:has(ul > li.active) { /* styles for nav with active list item */ }
Enter fullscreen mode Exit fullscreen mode

Enhancing CSS specificity

  • Improve the specificity of your selectors by targeting elements based on their content:
a:has(img) { /* styles for links with images */ }
Enter fullscreen mode Exit fullscreen mode

This helps override unintended styles applied to your elements.

Browser compatibility and limitations

While :has() is a powerful feature, browser support is crucial to consider. It enjoys good adoption with major browsers like Safari, Chrome, and Edge.

Here are some examples to illustrate the use of :has():

  • Style all <h1> elements that are followed by an <h2>element
h1:has(+ h2) {
  margin-bottom: 1.5rem;
}
Enter fullscreen mode Exit fullscreen mode
  • Add a border to paragraphs that contain an <code> element
p:has(code) {
  border: 1px solid #ddd;
  padding: 0.5rem;
}
Enter fullscreen mode Exit fullscreen mode

Utilizing :not() Selector

The :not() pseudo-class is a master of exclusion in CSS. It lets you target elements that don't match a specific selector or a list of selectors. This section explores the power of :not() and its various applications.

The :not() pseudo-class selects elements that are not represented by the selector(s) listed within its parentheses. It takes a simple selector or a comma-separated list of selectors as an argument.

Here's the basic syntax:

element:not(selector-list) {
  /* styles for elements that don't match the selector(s) */
}
Enter fullscreen mode Exit fullscreen mode

Use cases

:not() comes in handy for various targeting scenarios:

Selecting elements except certain ones

  • Style all list items <li> except those with a class of special
li:not(.special) {
font-size: 16px;
}
Enter fullscreen mode Exit fullscreen mode

Combining with other selectors for precise targeting

  • Target all links <a> within paragraphs <p> except those with a class of external
p a:not(.external) {
color: blue;
}
Enter fullscreen mode Exit fullscreen mode

Browser compatibility and limitations

:not() is a well-supported selector with broad browser compatibility across major browsers like Chrome, Firefox, Safari, and Edge. However, using a list of selectors within :not() might have slightly lower support, so it's advisable to check compatibility tables for the latest information.

Examples and code snippets

Here are some examples to showcase the power of :not():

  • Style all form elements <input>, <select> except for buttons <button>
form:not(button) {
border: 1px solid #ccc;
padding: 1rem;
}
Enter fullscreen mode Exit fullscreen mode
  • Add a hover effect to all images <img>except those inside a specific container with an ID of banner
img:not(#banner img) {
opacity: 0.8;
transition: opacity 0.2s ease-in-out;
}

img:not(#banner img):hover {
opacity: 1;
}
Enter fullscreen mode Exit fullscreen mode

Practical Applications of :has() and :not()

While :has() and :not() might seem like niche selectors, they offer a surprising range of practical applications in modern web development. Let's explore how these selectors can elevate your styles and enhance your website.

Responsive web design

  • Create responsive layouts that adapt to different screen sizes
body:not(.mobile) nav ul {  /* styles for nav on non-mobile screens */
display: flex;
}

body.mobile nav ul {  /* styles for nav on mobile screens */
display: block;
}
Enter fullscreen mode Exit fullscreen mode

Here, :not() is used to target styles specifically for non-mobile layouts.

Complex UI components

Style specific states of components based on their content:

.dropdown:has(.open) {  /* styles for dropdown when open */
background-color: #eee;
}
Enter fullscreen mode Exit fullscreen mode

:has() helps style dropdowns only when they contain an element with the class open.

Enhancing user experience

Provide visual cues based on element interactions:

button:not(:disabled):hover {/* hover effect for non-disabled buttons */
background-color: #ddd;
}
Enter fullscreen mode Exit fullscreen mode

:not() ensures hover effects are applied only to non-disabled buttons for a better user experience.

Performance optimization

Potentially reduce style recalculations by targeting elements with specific content:

/* General styles for all paragraphs */
p {
  color: #333;
}

/* Specific styles for paragraphs with warnings */
p:has(strong) {
  color: red;
}
Enter fullscreen mode Exit fullscreen mode

Best Practices and Tips for :has() and :not()

While :has() and :not() open doors for creative styling, it's important to wield them wisely. Here are some best practices to consider:

Keeping selectors efficient

Prioritize simpler selectors whenever possible. Complex selectors with :has() can be harder to understand and maintain.
If targeting elements based on a common ancestor, consider moving styles to that ancestor element instead of using :has().

Avoiding excessive specificity

Overly specific selectors can make your styles difficult to override. Use :has() and :not() strategically to achieve the desired level of specificity without going overboard.

Using fallbacks for browser compatibility

Not all browsers fully support :has(). Consider using media queries or feature detection to provide fallback styles for browsers that don't recognize :has().
You can also check Can I Use for the latest compatibility information.

Testing strategies

Thoroughly test your styles across different browsers and devices to ensure consistent rendering, especially when using :has() which might have limited support.

Consider using browser developer tools to inspect how your selectors are applied and identify any unexpected behavior.
By following these best practices, you can leverage the power of :has() and :not() while keeping your CSS clean, maintainable, and compatible across browsers. Remember, clarity and maintainability should always be a priority when crafting your CSS styles.

Conclusion

The exploration of :has() and :not() selectors has equipped you with valuable tools to elevate your CSS skills.

Let's recap the key takeaways:

  • :has() allows you to target elements based on their descendant elements.

  • :not() helps you select elements that don't match specific criteria.

Both selectors offer greater control over your styles and can improve responsiveness, UI component styling, and user experience.

While we've explored :has() and :not(), CSS offers a rich landscape of other advanced selectors. Don't be afraid to experiment with pseudo-classes like :hover, :focus, and attribute selectors to target elements based on various states and properties. The more you explore, the more control you'll have over your web page's appearance and interactivity.

Top comments (0)