Selectors are a core part of CSS. They allow you to do things like select all the elements of a certain type:
div {
/* some styles to apply to all div elements */
}
Or you can select an element that is the last child of its parent:
ul li:last-child {
/* some styles to apply to only the last child of a list */
}
Of course, they also allow you to do more complex things, like select all of the children of a list except its last child.
In fact, there’s more than one way to do that, some more complex than others.
For example, compare this:
ul li {
/* Styles to apply to all children */
}
ul li:last-child {
/* Styles to reset the previous styles because they don’t apply to the last child */
}
To this:
ul li:nth-last-child(n+2) {
/* Styles to apply to all children except the last one */
}
ul li
is an example of a level 1 selector.
last-child
and nth-last-child
are examples of level 3 selectors.
You can think of levels as versions of the CSS selector specification, where each level adds more powerful selectors.
In this article, I’ll give you an overview of the last generation of selectors, level 4, according to the Editor’s Draft specification as of January 2019, in the following categories:
At the time of this writing, the specification for level 4 selectors is in draft status. It may change until it reaches the official recommendation status, and you’ll find that many selectors are either not supported by some browsers or they need to be prefixed (with :-moz-
or :-webkit-
).
For each selector, I’ll give a link to its can I use page so you can see what browsers support it (if it’s available), a brief description, an example, and a link to a Codepen so you can try it (even if it doesn’t work right now, since it may change in the future).
With that in mind, let’s start with the selectors of the logical combinations category.
This category includes selectors that work by combining other selectors.
It selects elements that don’t match any of the selectors of the list that it takes as an argument. For example:
p:not(.beginning, .middle) {
color: red;
}
Will give all p
elements that don’t have the classes beginning
and middle
, a red color.
The level 3 version of this selector only allows one selector instead of a list of two or more. For example, the above style can be written as:
p:not(.beginning):not(.middle) {
color: red;
}
See the Pen
:not pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
It selects elements that match any of the selectors of the list that takes as an argument. For example:
p:is(.beginning, .middle) {
color: blue;
}
Will give all p
elements that have the classes beginning
and middle
, a blue color.
One of the most significant changes since the last working draft version is that the :matches() selector was renamed to :is() and deprecated it Safari (which was the only browser with full support for this selector), so it makes a better pairing with :not()
, its opposite.
:matches()
can be implemented as an alias for :is()
if needed for backwards-compatibility. However, :matches()
was formerly called :any()
, so most browsers support this pseudo-class with a prefix:
p:-webkit-any(.beginning, .middle) {
color: blue;
}
p:-moz-any(.beginning, .middle) {
color: blue;
}
See the Pen
:is pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
At the time of this writing, this selector is not supported by any browser yet.
It has the same syntax and functionality as :is()
, but neither the selector nor any of its arguments contribute to the specificity of the selector, which is always zero.
Specificity is the weight applied to a CSS rule. If two selectors apply to the same element, the one with higher specificity wins. If multiple rules have equal specificity, the last rule found in the CSS document is applied to the element.
This selector can be used to implement filters and override the style declarations associated with an element.
The specification gives the following example:
a:not(:hover) {
text-decoration: none;
}
nav a {
/* Has no effect */
text-decoration: underline;
}
/* With the new :where Level 4 selector */
a:where(:not(:hover)) {
text-decoration: none;
}
nav a {
/* Should work */
text-decoration: underline;
}
See the Pen
:where pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
Browser support (at the time of this writing, this selector is not supported by any browser yet.)
This selector takes a relative selector list as an argument. It selects an element if any of the relative selectors (when evaluated as elements in scope) match the element.
For example:
p:has(strong, em) {
color: red;
}
Will give all p
elements that contain either <strong>
or <em>
tags, a red color.
See the Pen
:has pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
This category includes selectors that work with an element’s attributes.
It selects an element whose foo
attribute value is exactly equal to bar
, regardless of its case.
For example:
p[class="text" i] {
color: green;
}
Will give all p
elements whose class
attribute has the values Text
, TEXT
, or text
, among other combinations, a green color.
See the Pen
Case-insensitive selector by Esteban Herrera (@eh3rrera)
on CodePen.
At the time of this writing, this selector is not supported by any browser yet.
It selects an element whose foo
attribute value is exactly and case-sensitively equal to bar
.
For example:
p[class="text" s] {
color: green;
}
Will give all p
elements whose class
attribute has the value text
(not Text
, for example), a green color.
See the Pen
Case-sensitive selector by Esteban Herrera (@eh3rrera)
on CodePen.
This category includes selectors that work with language-related settings.
It selects elements with left-to-right directionality, where the document language specifies how directionality is determined. On the other hand, :dir(rtl)
represents an element that has right-to-left directionality. Other values are not invalid but do not match anything.
For example:
p:dir(ltr) {
background-color: red;
}
Will set the background color of all p
elements with left-to-right directionality to red.
See the Pen
:dir(ltr) pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
Browser support (at the time of this writing, the level 4 version for this selector is not supported by any browser yet)
It selects an element tagged as being either in Chinese (or any other language, just replace zh
with any other language code) or written with traditional Chinese characters (or in any other character system, just replace *-hant
with any other character code).
Actually, this selector can be used since CSS 2, however, wildcard language matching and comma-separated lists are new in level 4 selectors.
It accepts a comma-separated list of one or more language ranges as its argument. If the language range contains asterisks, they must be either correctly escaped (:lang(es-\*)
) or quoted as a string (:lang("es-*)
).
For example:
p:lang("*-CH") {
background-color: red;
}
Will set the background color of all p
elements with one of the languages spoken in Switzerland (CH
represents Switzerland) to red.
See the Pen
:lang pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
This category includes selectors related to hyperlinks.
No can I use page, but this selector is supported by most of the major browsers.
It selects all elements that have an href
attribute (like <a>
or <link>
). In other words, all of the elements that match the :link
or :visited
pseudo-classes.
For example:
a:any-link {
color: red;
}
Will give all of the a
elements with an href
attribute, a red color.
See the Pen
:any-link pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
At the time of this writing, this selector is not supported by any browser yet.
It selects elements that target the current URL. If the link’s target includes a fragment URL, then the fragment URL of the current URL must also match; if it does not, then the fragment URL portion of the current URL is not taken into account in the comparison.
For example:
a:local-link {
text-decoration: none;
}
Will prevent all of the a
elements with an href
attribute that targets the current page from being underlined (maybe they are part of a navigation menu).
See the Pen
:local-link pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
This category includes selectors for elements the user is acting on.
It selects elements that either match the :focus
pseudo-class (when the element has the focus) or have children that match :focus
.
For example, assuming the following form:
<form>
<input type="text" id="name" placeholder="Enter your name" />
</form>
Will add a border around the input box when it receives focus.
form:focus-within {
border: 2px solid;
}
See the Pen
:focus-within pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
It selects an element if it’s currently focused (it matches the :focus
pseudo-class) and the browser determines that the focus should be made evident on the element, usually, with a focus ring.
The difference with :focus
is subtle.
If :focus-visible
matches, :focus
will also match, but the opposite is not always true, it depends on the browser (if it has focus ring drawing enabled) and how the element was focused (via a mouse click versus keyboard tabbing).
Firefox implements this selector as the :-moz-focusring
pseudo-class.
For example, in some cases, the following style:
/* Standard Level 4 selector */
:focus-visible {
background-color: lightgray;
}
/* Level 4 selector for Firefox*/
:-moz-focusring {
background-color: lightgray;
}
Will be only applied if the element receives the focus via keyboard tabbing.
See the Pen
:focus-visible pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
This category includes selectors that apply to elements that take user input.
:read-write
selects elements whose value is mutable (like an <input>
element that does not have the readonly
attribute).
:read-only
selects elements whose value is immutable (like an <input>
element that has the readonly
attribute).
However, these selectors don’t just select <input>
or <textarea>
elements, they select any element that can be edited by the user, such as a <p>
element with the contenteditable
attribute set to true
.
For example:
:read-write {
background-color: lightyellow;
}
:read-only {
background-color: lightgray;
}
/* For Firefox */
:-moz-read-write {
background-color: lightyellow;
}
:-moz-read-only {
background-color: lightgray;
}
Will set the background color of all mutable elements to lightyellow
and lightgray
for all immutable elements in a page.
See the Pen
:read-write and :read-only pseudo-classes by Esteban Herrera (@eh3rrera)
on CodePen.
It selects an input element that is currently showing placeholder text.
For example:
input:placeholder-shown {
color: red;
}
Will give the placeholder text of an <input>
element (only the placeholder text) a red color.
See the Pen
:placeholder-shown pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
It selects the element that is the default option in a group of related elements. Usually applies to buttons and select lists/menus.
For example:
input:default {
box-shadow: 0 0 2px 2px green;
}
input:default + label {
color: green;
}
Will give to the default <input>
element a green shadow and color to its label.
See the Pen
:default pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
It selects elements whose value is in an indeterminate state. For example, radio and checkbox groups that contain no checked elements, or progress bars whose percent completion is unknown.
For example:
input[type="radio"]:indeterminate + label {
color: red;
}
Will give the labels of the radio elements in a group, a red color if there are no elements in the group that are checked.
See the Pen
:indeterminate pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
They select elements whose contents or value is, respectively, valid or invalid according to their validity semantics. If the semantics are not defined (or cannot be defined), the element is neither :valid
nor :invalid
.
For example, for an input element of type email
:
input:invalid {
color: red;
}
input:valid {
color: green;
Will give the text of the element a different color depending if the element contains a valid or an invalid email.
See the Pen
:valid and :invalid pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
These selectors apply only to elements that have range limitations, for example, if it can have a defined minimum or maximum. If there are no such constraints, the element is neither :in-range
nor :out-of-range
.
For example, for an input element with a minimum value of 1 and a maximum value of 5:
input:out-of-range {
color: red;
}
input:in-range {
color: green;
}
Will give the element a red color for any values greater than 5, and a green color for values between 1 and 5.
See the Pen
:in-range and :out-of-range pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
In some cases, these selectors will have the same effect as :valid
and :invalid
.
These selectors apply to form elements that are, respectively, required or optional before the form can be submitted. Elements that are not form elements are neither required nor optional.
For example:
input:optional {
color: gray;
}
input:required {
color: red;
}
Will give the element required elements a red color and optional elements a gray color.
See the Pen
:required and :optional pseudo-class by Esteban Herrera (@eh3rrera)
on CodePen.
This category includes selectors that allow selection based on information that lies in the document tree but cannot be represented by other selectors, like the position of an element relative to its parent.
Browser support (at the time of this writing, the Level 4 version for this selector is not supported by any browser yet).
The :nth-child
selector matches every element that is the nth child of its parent. :nth-last-child
does the same but counting from the last child. You can play with this :nth tester to understand the difference and learn more about the An+B notation syntax.
At the beginning of this article, I showed you an example of :nth.last-child
and I said it was a Level 3 selector. However, for Level 4, this selector accepts an optional of
selector clause which filters the children to only those which match that selector.
In addition to being more powerful, sometimes, there can be differences due to the way the selector is declared. For example:
li.item:nth-child(-n+2)
Selects the first two <li>
elements but only if they have the class item
.
It’s not the same as:
:nth-child(-n+2 of li.item)
That selects the first two <li>
elements that have the class item
even if they are not the first two children on the list.
Try it (in a browser that supports this selector, like Safari):
See the Pen
:nth-child 1/2 by Esteban Herrera (@eh3rrera)
on CodePen.
See the Pen
:nth-child 2/2 by Esteban Herrera (@eh3rrera)
on CodePen.
This category includes selectors that work with columns of a table.
At the time of this writing, this selector is not supported by any browser yet.
The column combinator selector (||
) selects an element of type E
that represents a cell in a table and belongs to a column represented by an element of type F
.
For example, assuming the following table:
ID | Description | Price |
---|---|---|
1 | Computer | $999 |
2 | Tablet | $499 |
The following style:
col.selected || td {
background-color: lightyellow;
}
Will give cells (<td>
elements) that belong to the selected
column (Price) a light yellow background color.
See the Pen
Column combinator selector by Esteban Herrera (@eh3rrera)
on CodePen.
At the time of this writing, this selector is not supported by any browser yet.
You can think of these selectors as the column version of :nth-child
and :nth-last-child
.
:nth-col(An+B)
selects an element in a grid or a table that represents a cell of a column that has An+B-1
columns before it, for any positive integer or zero value of n
.
:nth-last-col(An+B)
selects an element in a grid or a table that represents a cell of a column that has An+B-1
columns after it, for any positive integer or zero value of n
.
For example:
col.nth-col(1) {
background-color: lightyellow;
}
col.nth-last-col(1) {
background-color: lightgreen;
}
Will give the first column of a table a light yellow background color, while the last column will have a light green background color.
See the Pen
:nth-col() and :nth-last-col() pseudo-classes by Esteban Herrera (@eh3rrera)
on CodePen.
Level 4 selectors allow you to declare complex selection rules in an easy way.
We have covered most of the selectors defined in the Editor’s Draft specification of January 2019, however, I have left out some selectors that are at risk of disappearing or changing soon, or there’s not much information about them (in addition to not being supported by any browser):
But definitely keep an eye on these and the rest of the Level 4 selector.
As web frontends get increasingly complex, resource-greedy features demand more and more from the browser. If you’re interested in monitoring and tracking client-side CPU usage, memory usage, and more for all of your users in production, try LogRocket.
LogRocket is like a DVR for web and mobile apps, recording everything that happens in your web app, mobile app, or website. Instead of guessing why problems happen, you can aggregate and report on key frontend performance metrics, replay user sessions along with application state, log network requests, and automatically surface all errors.
Modernize how you debug web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowLearn how to manage memory leaks in Rust, avoid unsafe behavior, and use tools like weak references to ensure efficient programs.
Bypass anti-bot measures in Node.js with curl-impersonate. Learn how it mimics browsers to overcome bot detection for web scraping.
Handle frontend data discrepancies with eventual consistency using WebSockets, Docker Compose, and practical code examples.
Efficient initializing is crucial to smooth-running websites. One way to optimize that process is through lazy initialization in Rust 1.80.