CSS Pseudo-Classes
Learning outcomes:
- What are pseudo-classes
- A simple example
- Commonly used pseudo-classes in CSS
- Interaction-based pseudo-classes —
:hover
,:active
, and:focus
- Structural pseudo-classes —
:nth-child()
,:nth-of-type()
, etc. - Other useful pseudo-classes —
:checked
,:disabled
,:target
, and:not()
In the last chapter, CSS Selectors, we learned a great deal about selectors in CSS; how they're used in styling given elements; the different kinds of selectors in CSS; and so on and so forth.
There, we briefly learned about a classification of simple selectors — pseudo-classes. This is what we shall explore in detail in this chapter.
In particular, we'll learn what exactly are pseudo-classes; why are they needed in addition to other simple selectors in CSS; some commonly used pseudo-classes in CSS; and much more.
What are pseudo classes?
Let's start with the definition:
A pseudo-class represents a characteristic of an element that is otherwise not representable by attributes on the element or least not as simply representable.
For example, consider an element that currently has the mouse pointer over it — this element has the characteristic of being 'hovered over.' Similarly, consider an input field that is disabled. This is another characteristic of the element.
In CSS, a pseudo-class is denoted using a :
(colon). The general syntax of a pseudo-class is :name
, where name
is the name of the pseudo-class.
Recall from the last chapter that a simple selector is a selector in CSS that can't be divided any further. An example is p
, a type selector selecting all <p>
elements in the document. A pseudo-class is also an instance of a simple selector.
Why are pseudo-classes are called 'pseudo'?
The word 'pseudo' has a straightforward meaning; it means 'fake', 'not genuine'.
In the concept of pseudo-classes, it signifies the fact that these are not 'real' classes of elements but rather created by the browser itself for specifically targeting certain aspects of elements and styling them.
It's now time for an example before we explore different kinds of pseudo-classes in CSS.
A simple example
Consider the following button:
<buton>A button</buton>
button {
background-color: white;
color: black;
padding: 10px;
font-size: 18px;
border: 1px solid black;
}
It just has a few basic styles on it in order to make it visually more pronounced:
Now suppose we want to change the background color of this button to black and the text color to white right when we bring the mouse pointer over it. This helps in immediately noticing that the button is interactive (i.e. could be clicked on).
How to accomplish this task?
Well, this is a special characteristic of the element — that is, when it is being hovered upon using the mouse pointer — which can't otherwise be handled by any HTML element, or any of its attributes. What we seek here is a pseudo-class.
Speaking of which, the :hover
pseudo-class represents an element in its hover state, i.e. when the mouse pointer is over the element.
:hover
is superbly common. You'll see it in every other webpage, probably because of its ubiquity.
Let's now accomplish the task above using :hover
.
Consider the following CSS with :hover
set up:
button:hover {
background-color: black;
color: white;
}
Before anything, hover over the button (i.e. bring the mouse pointer over it) below to see the effect of the pseudo-class in action:
Notice the background color changing? Behold, this is only the beginning of pseudo-classes; there's a lot we ought to cover in this chapter.
Coming back to the topic, in the code above, we've paired the :hover
pseudo-class with the button
type selector. This, in effect, means that we're selecting all buttons in their hover state.
:hover
isn't limited to just buttons; we can use it on any element in HTML (although it's more common to have it set up for interactive elements, like <button>
).
In the following code, we've set up the :hover
pseudo-class for a <p>
:
<p>This is a paragraph</p>
p {
background-color: pink;
padding: 10px;
}
p:hover {
background-color: yellow;
}
As before, hover over the paragraph below in order to witness the magic of the :hover
pseudo-class:
This is a paragraph
As you do so, you'll of course notice the background color changing to yellow. This is the power of :hover
, and most importantly of pseudo-classes.
With this example out of the way, it's now time to explore the huge world of pseudo-classes in CSS, followed by going over some more commonly used ones in the following sections.
List of pseudo classes
There are tons and tons of pseudo-classes in CSS at the time of this writing. And it's highly likely that more will cripple into CSS in the coming days, months, or years.
The discussion below enumerates some of the most common and useful pseudo-classes along with their purpose.
Because of the large number of selectors that are pseudo-classes, it's helpful to group them under different categories to make the comprehension a little bit easier and intuitive.
Here are all pseudo-classes that are tied to interacting with elements on a webpage. We've already seen a typical example of one of these above, that is, :hover
.
Pseudo-class | Meaning |
---|---|
:hover | The subject (i.e. the element selected by the entire selector, ending at :hover ) is in the hover state, i.e. the mouse pointer is over it. |
:active | The subject is about to receive focus. That is, when the mouse button is pressed (but not yet released) while the pointer is over the subject. |
:focus | The subject is in focus; henceforth, it can accept keyboard events on it. |
:focus-visible | Same as :focus in addition to the fact that the subject's focus needs to be clearly made visible to the user. |
:focus-within | One of the descendants of the subject is in focus. |
Apart from this, some pseudo-classes are tied to the structure of the underlying HTML document, i.e. which elements are inside of given elements, which elements have nothing inside of them, and so on. These are sometimes referred to as structural pseudo-classes.
The table below illustrates structural pseudo-classes:
Pseudo-class | Meaning |
---|---|
:root | The subject is the document's root element. For HTML document, it's <html> . |
:empty | The subject is empty, i.e. doesn't have any content in it. |
:nth-child(an + b) | The subject is the (an + b)th child of its parent. (We'll learn more about this in detail below.) |
:nth-last-child(an + b) | The subject is the (an + b)th last child of its parent. (We'll learn more about this in detail below.) |
:first-child | The subject is the first child of its parent. Same as :nth-child(1) . |
:last-child | The subject is the last child of its parent. Same as :nth-last-child(1) . |
:only-child | The subject is the only child of its parent. Same as :first-child:last-child . |
:nth-of-type(an + b) | The subject is the (an + b)th child of its parent of its type. (We'll learn more about this in detail below.) |
:nth-last-of-type(an + b) | The subject is the (an + b)th last child of its parent of its type. |
:first-of-type | The subject is the first child of its parent of its type. Same as :nth-of-type(1) . |
:last-of-type | The subject is the last child of its parent of its type. Same as :nth-last-of-type(1) . |
:only-of-type | The subject is the only child of its parent of its type. Same as :first-of-type:last-of-type . |
Besides structural pseudo-classes, some of these selectors are targeted towards form-related elements, which we can also refer to as input pseudo-classes.
Pseudo-class | Meaning |
---|---|
:required | The subject (an input element in this case) is required. |
:read-only | The subject is read-only. |
:checked | The subject (radio or checkbox) is checked. |
:disabled | The subject is disabled. |
:valid | The subject contains valid data. |
:invalid | The subject contains invalid data. |
:placeholder-shown | The subject has its placeholder shown (because it's empty). |
Not only this but we also have some logical pseudo-classes that further filter the subjects of a given selector:
Pseudo-class | Meaning |
---|---|
:is() | The subject matches the given selector list. |
:not() | The subject does not match the given selector list. |
:where() | The subject matches the given selector list. Same as :is() except for that :where() doesn't increase the specificity of the selector. (We'll learn more about specificity later in this unit, in CSS Style Precedence.) |
:has() | The subject has given elements inside of it, as identified by the given relative selector list. |
Apart from this, there are some location-based pseudo-classes:
Pseudo-class | Meaning |
---|---|
:link | The subject is a hyperlink that hasn't been visited yet. |
:local-link | The subject is a hyperlink whose URI is the same as the current document's URI. |
:visited | The subject is a hyperlink that has been visited. |
:target | The subject is the document's target, i.e. the element whose ID is the same as the document's fragment as pointed to by its URI. |
Let's now start exploring some of the more common of these pseudo-classes in the sections below.
Interaction-based pseudo classes
:hover
Even though we've explored :hover
in the preceding discussion, let's have a look at it again. It's so common in webpages that it wouldn't hurt witnessing it once again. What do you say?
The :hover
pseudo-class represents an element in its hover state, that is, when the element has the mouse pointer over it.
Dynamic pseudo-classes
Some pseudo-classes in CSS are dynamic. That is, they can apply or no longer apply to given elements as we interact with them. :hover
is an example of a dynamic pseudo-class.
Dynamic pseudo-classes basically arise from characteristics of elements that are dynamic in themselves.
For example, reconsider the 'hovered over' characteristic. Obviously this only applies so long as the mouse pointer is over the element in question. Likewise, the moment the characteristic disappears, i.e. the mouse pointer goes outside the element, so does the corresponding :hover
pseudo-class.
Here's a practical example for :hover
:
<button>This is a button</button>
<button>This is another button</button>
button:hover {
background-color: blueviolet;
color: white;
}
It's worthwhile noting here that although :hover
can be set up on any HTML element, it's more sensible to only do so on interactive elements, or at least mostly on them.
This is because it's only mostly on interactive elements that we need to provide visual feedback (that those elements are interactive).
It doesn't really make much sense to set up :hover
on non-interactive elements like <p>
, <h1>
, etc. as the user is most likely to not assume them as interactive (and rightly shouldn't).
:active
The :active
pseudo-class represents the state whereby an element is about to be brought into focus.
More specifically, when we press the mouse button, but not release it, while the pointer is over an element, that element is matched by :active
.
Let's bring in the same example as above for :hover
, just this time replacing :hover
with :active
:
button:active {
background-color: blueviolet;
color: white;
}
Open up the example below and try hovering over a <button>
, expecting nothing to happen. Then, click on the <button>
but make sure to not let go off the mouse button as you do so. Then finally release the mouse button.
What did you notice throughout this series of interactions?
Well, as soon as the mouse button is clicked, the <button>
becomes active, and thus the :active
pseudo-class applies. But as soon as the mouse button is released, the pseudo-class ceases to apply.
Quite simple.
The :active
pseudo-class is handy when we want to style buttons (or other interactive elements) differently when they are depressed (pushed down).
As an example, consider the following:
Clearly, we haven't covered much of the stylistic properties being used in this example but the idea is evident — as we depress the button, we get a familiar depressed style.
:active
pseudo-class also applies when we press the spacebar key while an interactive element has focus. Precisely speaking, so long as the spacebar key stays depressed, the pseudo-class applies; the moment we release the key, the pseudo-class ceases to apply (similar to releasing the mouse button).:focus
The :focus
pseudo-class applies to elements when they receive focus.
Speaking of which, an element receives focus when it is clicked upon or when it is tabbed to (i.e. navigated to using the Tab key) and if the element is focusable.
<p>
is not focusable by default and therefore can't take up the :focus
pseudo-class.Keep in mind that a focusable element receives focus as soon as the mouse button goes down while the pointer is over it; the click action doesn't have to be a complete click, so to speak.
In this way, :focus
is similar to :active
except for that :focus
remains as it is even after the mouse button is released unlike :active
which, as we know, goes away.
As before, let's reconsider the same <button>
s example, this time obviously replacing :hover
with :focus
:
button:focus {
background-color: blueviolet;
color: white;
}
The :focus
pseudo-class is handy when we want to specifically style elements when they receive focus. This is more important than you might think right now. But how?
Well, the reason is because not everyone uses the mouse to navigate around a webpage; sometimes, the keyboard is used instead which means that the user must be clearly presented with visual cues as to which element on the webpage is currently under focus.
Thus, it won't be wrong to say that pseudo-classes help improve the accessibility of a webpage by providing visual cues as users interact with elements.
:focus-visible
and :focus-within
The two pseudo-classes, :focus-visible
and :focus-within
, are quite similar to :focus
but certainly not identical. They differ in subtle, yet important ways.
If you'd like to learn more about these two selectors, you're encouraged to read the article What is the Difference Between :focus
, :focus-visible
, and :focus-within
in CSS?.
Structural pseudo-classes
Structural pseudo-classes have to do everything with the structure of the HTML document. Let's see some of the most commonly used pseudo-classes in this category.
:nth-child()
The :nth-child()
pseudo-class applies to any element that is literally the nth child of its parent.
For example, :nth-child(1)
applies to all elements that are the first child elements of their parent. Similarly, :nth-child(10)
applies to all elements that are the tenth child elements of their parent.
Consider the following HTML code:
<div>
<p>The first paragraph</p>
<p>The second paragraph</p>
</div>
Here, the <div>
contains two <p>
children. p:nth-child(1)
would only, however, apply to the first <p>
.
The CSS below confirms this:
p:nth-child(1) {
background-color: yellow;
}
:nth-child(0)
doesn't select anything.:nth-child()
and all other structural selectors.The real potential of :nth-child()
lies in the fact that it could be provided an expression to refer to multiple children in one go. This expression, akin to the expression for computing the nth term of an arithmetic sequence, is of the general form:
an + b
Here, a
and b
are both provided by us; n
on the otherhand is itself plugged into the expression by CSS itself in order to get to concrete number. The value of n
begins at 0
and increments by 1
.
So, 3n + 1
gives us 1
, 4
, 7
, 10
, and so on. :nth-child(3n + 1)
is equivalent to manually setting up each of :nth-child(1)
, :nth-child(4)
, :nth-child(7)
, and so on.
Let's take an example.
Which positions does the pseudo-class :nth-child(3n)
target?
- 1, 2, 3, 4, ...
- 3, 6, 9, 12, ...
- 4, 7, 10, 13, ...
The expression 3n
means the positions 3 (when n = 1
), 6 (when n = 2
), 9, 12, 15, and so on.
Suppose we want to select all even-positioned <tr>
elements (inside a <tbody>
element) in a table. This is a real, practical scenario, used to give a stripe style to a table, i.e. even and odd rows with differing backgrounds.
Anyways, coming back to the task, to select all even positioned elements, we need to target elements in the 2nd, 4th, 6th, 8th, ..., positions. This means that the expression would be 2n
(remember that n
begins at 0
).
The following CSS code demonstrates such an example for a table along with some other styling as well:
th {
background-color: gray;
color: white;
}
tr:nth-child(2n) {
background-color: lightgray;
}
Notice how the 2nd and 4th rows, following the table's header, have the lightgray background, courtesy of the :nth-child(2n)
selector.
The special keywords even
and odd
Since 2n
and 2n + 1
are both commonly used expressions with regards to :nth-child()
, CSS has handy shortcuts for them: even
and odd
, respectively.
Hence, :nth-child(2n)
is the same as :nth-child(even)
. Similarly, :nth-child(2n + 1)
is the same as :nth-child(odd)
.
:nth-last-child()
The :nth-last-child()
selector is identical to :nth-child()
except for that it, as you can guess, starts from the end of a given element in enumerating its children.
So where :nth-child(1)
selects the first element child of every element, :nth-last-child(1)
selects the last element child (which is the first element child from the end) of every element.
And just like, :nth-child()
, :nth-last-child()
also supports an expression of the form an + b
(with the exact same behavior but obviously from the end of the parent element).
:first-child
and :last-child
Because :nth-child(1)
and :nth-last-child(1)
are common pseudo-classes, CSS provides us with concise selectors to accomplish similar effects: :first-child
and :last-child
, respectively.
:nth-of-type()
Where :nth-child()
selects children elements in given positions, :nth-of-type()
accomplishes the same thing albeit it only focuses on the children of a given type (i.e. tag name).
For example, as we know, :nth-child(2)
would target the second element children of their parents. In contrast, :nth-of-type(2)
would target second element children of their parents from the list of all children of the given type.
If this is unclear, the following example will hopefully clarify the confusion. (Well, honestly speaking, an example is really needed over here.)
Consider the following <div>
:
<div>
<p>The first paragraph</p>
<blockquote>The first blockquote</blockquote>
<p>The second paragraph</p>
</div>
First, take the list of all children elements of this <div>
. We have a <p>
, followed by a <blockquote>
, followed by another <p>
.
What is the first element in this list?<p>
, right? This is precisely what gets selected by p:nth-child(1)
.
Now, take the list of all children elements of this <div>
that are of type <blockquote>
. We have <blockquote>
, and that's it. Alright.
What is the first element in this list?<blockquote>
, right? And there you have it. This is precisely what gets selected by blockquote:nth-of-type(1)
.
The <blockquote>
element would NOT be matched by the blockquote:nth-child(1)
selector because it isn't the first child of its parent. However, it would certainly be matched by blockquote:nth-of-type(1)
because it indeed is the first <blockquote>
element in its parent; in other words, there is no <blockquote>
before it.
Notice the subtle but important distinction between :nth-child(1)
and :nth-of-type(1)
. The former targets the first child element from the list of all child elements. In comparison, the latter targets the first child element from the list of all <blockquote>
child elements.
Here's some CSS code to style the first <blockquote>
:
blockquote:nth-of-type(1) {
background-color: yellow;
}
Had the selector here instead been blockquote:nth-child(1)
, nothing would have been selected because the <blockquote>
element in the code above isn't the first child element of its parent; it's only the first child <blockquote>
element.
Here's a quick quiz to solve.
For the same HTML code above, what would p:nth-of-type(2)
select?
- Nothing
<p>First paragraph</p>
<p>Second paragraph</p>
It would select the second paragraph because it's indeed the second <p>
child element of its parent (but obviously isn't the second child element; that is <blockquote>
).
Moving on, akin to :nth-child()
and :nth-last-child()
, :nth-of-type()
also supports an expression of the form an + b
.
:nth-last-of-type()
, :first-of-type
, and :last-of-type
Similar to how we have corresponding classes for :nth-child()
, so do we for :nth-of-type()
.
They are :nth-last-of-type()
, :first-of-type
, and :last-of-type
. Once you understand :nth-child()
, :nth-last-child()
, and :nth-of-type()
, there's a 100% probability that you'd be able to understand these three selectors.
For brevity, I'll be skipping them but you're encouraged to play and experiment around with them on your end. Experimentation is healthy!
Other useful pseudo-classes
:checked
The :checked
pseudo-class is amongst the category of pseudo-classes that apply to input elements (<input>
s, <button>
s, <textarea>
s, etc.).
Speaking more about it, :checked
applies when an input element — more precisely, a radio or a checkbox — is checked.
Consider the following example:
<label>
<input type="checkbox" name="terms">
<span>I agree to the terms and conditions</span>
</label>
We have a simple checkbox input inside of a <label>
, part of a larger (hypothetical) form where the user creates a new account on an online service.
Let's style this checkbox differently when it's checked.
Now because a checkbox has a special default appearance on browsers, which can't otherwise be modified by properties such as color
, background-color
, or border
, we need some property that we could easily apply to it.
This property can be opacity
. We'll learn more about opacity
in CSS Colors — Opacity but the simple explanation of it is that it controls the opacity (how much is visible) of a given element.
To be more specific, opacity
takes a number between 0
and 1
, both inclusive, which tells about the opacity of the element. 0
means nothing is visible (although the element is right there in the document) whereas 1
means complete visibility (the default).
In our example, let's set the opacity
of the checkbox to 0.3
to make it near invisible when it's checked, obviously leveraging the :checked
pseudo-class:
input:checked {
opacity: 0.3;
}
As you click on the checkbox, notice its appearance — the opacity: 0.3
style would reduce it considerably.
:checked
is quite useful a pseudo-class in CSS. It can be used to create customized radio buttons and checkboxes in HTML.
Take a look at the example below to see what's possible using the :checked
pseudo-class, as an opportunity to convince you further about the breadth of knowledge you'll gain in this course:
The specifics of this advanced styling require us to know about some other CSS properties which we will eventually as this course progresses further.
But what we can take away right now from the example is that: CSS is exceptionally powerful!
:disabled
The :disabled
pseudo-class applies when an input element is disabled.
In other words, when an input element has the disabled
attribute set on it (and contains the value "true"
or just exists as a Boolean attribute), the input element matches :disabled
.
Shown below is an example using a <button>
:
<button>A normal button</button>
<button disabled>A disabled button</button>
Here, we have two buttons of which one is disabled. The following CSS styles this disabled button specially:
button {
background-color: blue;
color: white;
border: 2px solid navy;
font-size: 18px;
}
button:disabled {
opacity: 0.3;
}
Because disabled form fields aren't able to be interacted with, it's a wise decison to reduce their opacity considerably down. The CSS rule above for the pseudo-class :disabled
does exactly this.
Browsers, by default, style disabled form fields differently. In particular, they change their background color and text color to produce a 'grayed-out' effect.
Go on and see this default style by creating a <button>
in a blank HTML document and then disabling it by setting the disabled
attribute.
:target
The :target
pseudo-class applies to the target of the underlying HTML document. So what exactly is the target?
Well, the target is the element referred to by the URI of the document. But how can a URI refer to a particular element? Hmm... You already know this. Let me make this simpler for you. You already know this from the HTML URIs chapter.
When the URI of the HTML document contains a fragment in it, it refers to a particular element in the document — an element whose ID matches the fragment identifier in the URI.
So, for example, suppose that we have the URI https://example.com/#p1, with the fragment as #p1, and the following <p>
element:
<p id="p1">A paragraph</p>
Visiting this URI would, likewise, have the underlying document's target as the shown <p>
element because its ID matches the fragment identifier.
Now, often, it's desirable to uniquely style the target of a document so that the user clearly knows which specific element the given URI points to. This is something that can easily be done with the help of the :target
pseudo-class.
An example follows.
In the HTML below, we have a hyperlink to go to a fragment in the same document. Also, we have a <div>
with id="d1"
set, which is what the hyperlink points to:
<p><a href="#d1">Go to #d1</a></p>
<div id="d1">This is the #d1 div</div>
It's all pretty basic of a setup. Now, let's apply a yellow background to the target element in case the document's URI contains a fragment:
:target {
background-color: yellow;
}
Open up the linked document and click on the hyperlink:
As you do so, notice the background color of the <div>
changing to yellow because of it becoming the target of the document.
:not()
The :not()
selector is another instance of a functional selector in that it accepts arguments.
As the name suggests, :not()
applies to elements that do NOT match a given list of selectors; this list is provided within the pair of parentheses (()
).
For example, suppose we want to match all <p>
elements that don't have the class wrong
on them. This can be accomplished using the compound selector p:not(.wrong)
. Here, .wrong
is provided to :not()
because it's the selector that should not be matched by the selected <p>
elements.
Spread the word
Think that the content was awesome? Share it with your friends!
Join the community
Can't understand something related to the content? Get help from the community.