The Current Generation of CSS3 Selectors

Share this article

After CSS 2.1, the W3C added a whole slew of new selectors to help us target elements in unique ways. Just about all of these selectors have excellent browser support, so you can — and probably should — already be using them.

If you’re not too familiar with them, this article will give you an overview of these CSS3 selectors, which I’d like to term the ‘current generation’ of CSS selectors. Where applicable, I’ve included a simple embedded demo demonstrating the selector.

The :target Pseudo-class

The :target pseudo-class styles an element if it has been selected via a fragment identifier in the URL. If that sounds confusing, let’s see an example. Here’s a URL as it would appear in the address bar:

https://www.sitepoint.com/example-page/#section-1

This URL targets an element that has an ID of “section-1”, scrolling the page to that location. You’ve likely done this before with what might be informally called in-page links or local links.

When you declare a :target pseudo-class in your CSS, any element that is ‘targeted’ in this manner in the URL will be styled as defined in the :target rule set:

:target {
    background-color: yellow;
    color: black;
}

In this case, the :target selector is being used universally (that is, it’s not attached to any specific element), which is fine. We could however, limit it to only certain elements:

p:target {
    background-color: yellow;
    color: black;
}

You’ve probably already seen this selector in action on Wikipedia footnote references, as shown in the screenshot below:

Wikipedia footnotes

See the Pen :target Demo by SitePoint (@SitePoint) on CodePen.

:enabled and :disabled pseudo-classes

User interface elements can exist in different ‘states’. Two of those states are enabled and disabled. While this is not limited to form elements, more than likely this is how you will recognize the concept of enabled and disabled.

CSS now includes pseudo-classes to style elements based on the two aforementioned states:

input:enabled {
    background: white;
    border: solid 1px #000;
}

input:disabled {
    background: #ccc;
    border: solid 1px #aaa;
}

This makes it easier for users to distinguish between stuff that’s clickable and stuff that’s not. A “disabled” state is identified on form elements by adding the Boolean “disabled” attribute. Many browsers automatically style elements a specific way when this attribute is present, but using this pseudo-class you can add your own styles if desired.

See the Pen :disabled and :enabled by SitePoint (@SitePoint) on CodePen.

:checked and :indeterminate pseudo-classes

Similar to enabled/disabled, checkbox elements and radio buttons on forms can have two basic states: selected (or “checked”) and unselected (or “unchecked”). CSS lets you style elements in these states using the :checked pseudo-class:

:checked {
    margin-left: 20px;
}

Similar to enabled/disabled, this one can be changed via the “checked” or “selected” attributes in the HTML.

See the Pen :checked by SitePoint (@SitePoint) on CodePen.

There is, however, another state for such elements: The “indeterminate” state. This state exists if the user has yet to act on the element, thus the element has not been explicitly set to either checked or unchecked. The :indeterminate pseudo-class is mentioned in the official W3C spec, but it has not yet been described in detail or implemented in any browser.

:nth-child() pseudo-class

This pseudo-class has a full range of possibilities defined by a value or expression that you insert into the parentheses. The simple way to use it is via an integer value or the keywords odd or even:

div:nth-child(1) {
    background: #eee;
}

tr:nth-child(odd) {
    background: #bbb;
}

The first rule set in the above example will style any <div> element that is the first of its siblings. That is, no element appears before it inside of its parent.

In the second rule set, we’re targeting all odd numbered <tr> elements (table rows), adding a background color to them. This lets you do zebra-striping on tables easily without having to resort to JavaScript or hard-coding multiple classes into the HTML.

In addition to those simple values, the :nth-child() pseudo-class can also take an expression as a value:

li:nth-child(4n+2) {
    background: #ccc;
}

In this example, we’re adding a background color to a set of specified list elements. But which elements? In this specific case (4n+2), the expression translates to:

Select the 2nd list element and every 4th element after that

And this would apply separately to every list. So if you had 3 lists on a single HTML page, this selector would select the second element in each of those lists, as well as every fourth element after the 2nd element in each list. So the count starts new for each set of targeted elements inside its respective parent.

You can also use a negative integer for either value. Let’s try a negative integer on the part of the expression that defines the first selected element:

li:nth-child(4n-1) {
    background: #ccc;
}

In this case, the first element to have the background color applied would be the 3rd actual list element. This is because of the negative value (-1). Counting 4 elements after the theoretical -1st value brings us to the 3rd value (which includes counting a theoretical “zero” value along the way).

Using a negative value on the first part of the expression, however, results in the elements being selected backwards starting with the first selected element, like this:

li:nth-child(-4n+5) {
    background: #ccc;
}

This expression selects the 5th element and also every 4th element prior to that element. This means nothing gets selected after the 5th element.

You’ll definitely want to fiddle with these expressions using both positive and negative values for both sides of the equation to get a good understanding of how these work in practice.

See the Pen :nth-child demo by SitePoint (@SitePoint) on CodePen.

:nth-last-child(), :nth-of-type(), :nth-last-of-type()

These next three pseudo-classes work exactly the same way as :nth-child(), accepting an integer, a keyword value of “odd” or “even”, or an expression. But they each have a different premise:

  • :nth-last-child() always starts selecting from the bottom.
  • :nth-of-type() will select only elements matching the “type” you specify (e.g. p:nth-of-type(3) will select only the third paragraph element, ignoring any elements between the paragraphs).
  • :nth-last-of-type() is like a combination of the previous two. It selects starting with the last element of the specified type.

See the Pen :nth-last-child demo by SitePoint (@SitePoint) on CodePen.

:first-child and :last-child pseudo-classes

These selectors are pretty self-explanatory. They select elements that are the first or last child of their parent element. For example, if you have four sets of unordered lists on a page, we could apply the following CSS:

li:first-child {
    background: #444;
    color: white;
}

li:last-child {
    background: #555;
    color: #ccc;
}

For each separate list, the first and last list items will be styled as specified, but the other list items will not be affected.

See the Pen :first-child, :last-child by SitePoint (@SitePoint) on CodePen.

:first-of-type and :last-of-type

Similar to :first-child and :last-child, these will select the first and last items from a set of sibling elements, but the difference is that they will select based on the “type” of element specified:

p:first-of-type {
    margin-top: 3em;
}

p:last-of-type {
    margin-top: 0;
}

In the above example, the page in question may have other elements before or after the paragraphs. Those elements will be ignored, and only the first and last paragraphs will be selected and styled.

See the Pen :first-of-type, :last-of-type by SitePoint (@SitePoint) on CodePen.

:only-child and :only-of-type

These two selectors select based on whether or not the specified element is the only child or the only child of that type. For example:

li:only-child {
    background: goldenrod;
}

p:only-of-type {
    margin: 20px;
}

In the first rule set, a list item will be styled only if it is the sole list item in a list. If there are two or more list items in the list, the styles will not apply.

Likewise, the second rule set will apply its styles only if the specified type of element (in this case a paragraph) is the only one of its type inside its parent element. There might be another type of element inside the parent element (e.g. a <section> or <img>) but those other elements will be ignored. If there is more than one paragraph element, no element will receive the styles.

See the Pen :only-child, :only-of-type by SitePoint (@SitePoint) on CodePen.

:empty

The :empty pseudo-class selects an element if it has no children. Take the following HTML snippet:

<p>Example paragraph.</p>

<p></p>

<p>Example paragraph.</p>

And then the following CSS:

p:empty {
    display: none;
}

The display: none declaration will apply only to the second <p> element in the HTML. This is because it has no children (i.e. it’s empty).

See the Pen :empty demo by SitePoint (@SitePoint) on CodePen.

::first-letter and ::first-line pseudo-elements

These selectors are called pseudo-elements. They select imaginary, or implied elements, on the page. The :first-letter pseudo-element can be used to create a drop-cap effect:

p {
  font-family: Georgia, sans-serif;
  font-size: 1.5em;
}

p:first-of-type::first-letter { 
  float: left;
  color: goldenrod;
  font-size: 90px; 
  line-height: 70px;
  padding-right: 7px;
  font-family: Georgia;
}

In this example, all paragraph elements have a couple of universal styles (font and font-size). But then we combine a pseudo-class (:first-of-type) with the ::first-letter pseudo-element to target the first letter in the first paragraph.

The ::first-line pseudo-element works similarly, this time styling the entire first line, as it appears in the browser:

p:first-of-type::first-line { 
    text-transform: uppercase;
}

And keep in mind that if the element’s width is flexible, the browser will always ensure that no matter what size the lines are, the first line will always be the only one selected and styled.

It should also be noted that although these two selectors use the double-colon syntax (to differentiate pseudo-elements from pseudo-classes), some older browsers support them using the single-colon syntax.

See the Pen :first-letter, :first-line demo by SitePoint (@SitePoint) on CodePen.

Note: Chrome doesn’t recognize text-transform: uppercase when applied to the ::first-line pseudo-element. This is a known bug.

:not() pseudo-class

This pseudo-class is referred to as the negation pseudo-class. This selector tells the browser to style all elements except the ones specified inside the parentheses. The negated elements are specified using an element type selector, class selector, id selector, attribute selector, or pseudo-class.

For example:

button:not([disabled]) {
    color: #white;
}

:not(div) {
    background: #ccc;
}

In the first declaration block we are targeting all button elements that do not have the “disabled” attribute in the HTML. In the second declaration block we are targeting all elements that are not <div> elements.

See the Pen :not demo by SitePoint (@SitePoint) on CodePen.

Browser Support?

As mentioned, with the exception of :indeterminate, these selectors have excellent support in modern browsers. They are supported everywhere including IE9 and above. So the only problem browsers are older versions of Internet Explorer. And if you don’t mind giving old IEs an extra script, you can try Selectivizr to patch up the lack of support.

Conclusion

I hope you enjoyed this review of the current generation of selectors. If you’ve used these often or know of any browser quirks worth mentioning, feel free to add a comment.

Louis LazarisLouis Lazaris
View Author

Louis is a front-end developer, writer, and author who has been involved in the web dev industry since 2000. He blogs at Impressive Webs and curates Web Tools Weekly, a newsletter for front-end developers with a focus on tools.

CSSCSS3pseudo-classespseudo-elementsselectors
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week