In this, the third in our series of articles on selectors, we discuss pseudo-selectors — these don't select actual elements, but rather certain parts of elements, or elements only in certain contexts. They come in two main types — pseudo-classes and pseudo-elements.
Pseudo-classes
A CSS pseudo-class is a keyword preceded by a colon (:
) that is added on to the end of selectors to specify that you want to style the selected elements only when they are in certain state. For example you might want to style an element only when it is being hovered over by the mouse pointer, or a checkbox when it is disabled or checked, or an element that is the first child of its parent in the DOM tree.
:active
:any
:checked
:default
:dir()
:disabled
:empty
:enabled
:first
:first-child
:first-of-type
:fullscreen
:focus
:hover
:indeterminate
:in-range
:invalid
:lang()
:last-child
:last-of-type
:left
:link
:not()
:nth-child()
:nth-last-child()
:nth-last-of-type()
:nth-of-type()
:only-child
:only-of-type
:optional
:out-of-range
:read-only
:read-write
:required
:right
:root
:scope
:target
:valid
:visited
We will not dig into every pseudo-class right now — it is not the object of the Learning Area to teach you everything in exhaustive detail, and you'll certainly come across some of these in more detail later on where appropriate.
A pseudo-class example
For now, let's just see a simple example of how to use these. First, an HTML snippet:
<a href="https://developer.mozilla.org/" target="_blank">Mozilla Developer Network</a>
Then, some CSS rules:
/* These styles will style our link in all states */ a { color: blue; font-weight: bold; } /* We want visited links to be the same color as non visited links */ a:visited { color: blue; } /* We highlight the link when it is hovered (mouse), activated or focused (keyboard) */ a:hover, a:active, a:focus { color: darkred; text-decoration: none; }
and now let's play with the link we just styled!
Active learning: A zebra striped info list
Your turn again — in this active learning segment we'd like you to add some zebra striping to a list of info links, and also add an appropriate pseudo-classes to style the links differently when they are hovered over. You'll only need to edit the last three rules in this exercise. Some hints:
- You have already seen how to write the second pseudo class for the hover styles.
- For the zebra striping you'll need to use a pseudo class like
:nth-of-type()
, giving the two color rules a slightly different version of the pseudo class to style the even and odd numbered list items. See if you can look up how to do this!
If you make a mistake, you can always reset it using the Reset button. If you get really stuck, press the Show solution button to see a potential answer.
Playable code 5
<div class="body-wrapper" style="font-family: 'Open Sans Light',Helvetica,Arial,sans-serif;"> <h2>HTML Input</h2> <textarea id="code" class="html-input" style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;"><ul> <li><a href="#">United Kingdom</a></li> <li><a href="#">Germany</a></li> <li><a href="#">Finland</a></li> <li><a href="#">Russia</a></li> <li><a href="#">Spain</a></li> <li><a href="#">Poland</a></li> </ul></textarea> <h2>CSS Input</h2> <textarea id="code" class="css-input" style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;">ul { padding: 0; } li { padding: 3px; margin-bottom: 5px; list-style-type: none; } a { text-decoration: none; color: black; } { text-decoration: underline; color: red; } { background-color: #ccc; } { background-color: #eee; }</textarea> <h2>Output</h2> <div class="output" style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;overflow:auto;"></div> <div class="controls"> <input id="reset" type="button" value="Reset" style="margin: 10px 10px 0 0;"> <input id="solution" type="button" value="Show solution" style="margin: 10px 0 0 10px;"> </div> </div>
var htmlInput = document.querySelector(".html-input"); var cssInput = document.querySelector(".css-input"); var reset = document.getElementById("reset"); var htmlCode = htmlInput.value; var cssCode = cssInput.value; var output = document.querySelector(".output"); var solution = document.getElementById("solution"); var styleElem = document.createElement('style'); var headElem = document.querySelector('head'); headElem.appendChild(styleElem); function drawOutput() { output.innerHTML = htmlInput.value; styleElem.textContent = cssInput.value; } reset.addEventListener("click", function() { htmlInput.value = htmlCode; cssInput.value = cssCode; drawOutput(); }); solution.addEventListener("click", function() { htmlInput.value = htmlCode; cssInput.value = 'ul {\n padding: 0;\n}\n\nli {\n padding: 3px;\n margin-bottom: 5px;\n list-style-type: none;\n}\n\na {\n text-decoration: none;\n color: black;\n}\n\na:hover {\n text-decoration: underline;\n color: red;\n}\n\nli:nth-of-type(2n) {\n background-color: #ccc;\n}\n\nli:nth-of-type(2n+1) {\n background-color: #eee;\n}'; drawOutput(); }); htmlInput.addEventListener("input", drawOutput); cssInput.addEventListener("input", drawOutput); window.addEventListener("load", drawOutput);
Pseudo-elements
Pseudo-elements are very much like pseudo-classes, but they have differences. They are keywords — this time preceded by two colons (::
) — that can be added to the end of selectors to select a certain part of an element.
They all have some very specific behaviors and interesting features but digging into them all in detail is beyond scope for now.
A pseudo-element example
Here we'll just show a simple CSS example that selects the space just after all absolute links and adds an arrow in that space:
<ul> <li><a href="https://developer.mozilla.org/en-US/docs/Glossary/CSS">CSS</a> defined in the MDN glossary.</li> <li><a href="https://developer.mozilla.org/en-US/docs/Glossary/HTML">HTML</a> defined in the MDN glossary.</li> </ul>
Let's add this CSS rule:
/* All elements with an attribute "href", which values start with "http", will be added an arrow after its content (to indicate it's an external link) */ [href^=http]::after { content: '⤴'; }
and we get this result:
Active learning: A fancy paragraph
Next up for active learning, we have a fancy paragraph to style! All you've got to do here is apply the two rulesets to the paragraph's first line and first letter, using the ::first-line
and ::first-letter
pseudo elements. This should style the first line of the paragraph in ALL-CAPS and the first letter as a nice drop cap, giving it an old-style feel.
If you make a mistake, you can always reset it using the Reset button. If you get really stuck, press the Show solution button to see a potential answer.
Playable code 6
<div class="body-wrapper" style="font-family: 'Open Sans Light',Helvetica,Arial,sans-serif;"> <h2>HTML Input</h2> <textarea id="code" class="html-input" style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;"><p>This is my very important paragraph. I am a distinguished gentleman of such renown that my paragraph needs to be styled in a manner befitting my majesty. Bow before my splendour, dear students, and go forth and learn CSS!</p></textarea> <h2>CSS Input</h2> <textarea id="code" class="css-input" style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;"> { text-transform: uppercase; } { font-size: 3em; border: 1px solid black; background: red; display: block; float: left; padding: 2px; margin-right: 4px; }</textarea> <h2>Output</h2> <div class="output" style="width: 90%;height: 10em;padding: 10px;border: 1px solid #0095dd;overflow:auto;"></div> <div class="controls"> <input id="reset" type="button" value="Reset" style="margin: 10px 10px 0 0;"> <input id="solution" type="button" value="Show solution" style="margin: 10px 0 0 10px;"> </div> </div>
var htmlInput = document.querySelector(".html-input"); var cssInput = document.querySelector(".css-input"); var reset = document.getElementById("reset"); var htmlCode = htmlInput.value; var cssCode = cssInput.value; var output = document.querySelector(".output"); var solution = document.getElementById("solution"); var styleElem = document.createElement('style'); var headElem = document.querySelector('head'); headElem.appendChild(styleElem); function drawOutput() { output.innerHTML = htmlInput.value; styleElem.textContent = cssInput.value; } reset.addEventListener("click", function() { htmlInput.value = htmlCode; cssInput.value = cssCode; drawOutput(); }); solution.addEventListener("click", function() { htmlInput.value = htmlCode; cssInput.value = 'p::first-line {\n text-transform: uppercase;\n}\n\np::first-letter {\n font-size: 3em;\n border: 1px solid black;\n background: red;\n display: block;\n float: left;\n padding: 2px;\n margin-right: 4px;\n}'; drawOutput(); }); htmlInput.addEventListener("input", drawOutput); cssInput.addEventListener("input", drawOutput); window.addEventListener("load", drawOutput);
Next up
We will round off our tour of CSS selectors by looking at Combinators and multiple selectors.