Advanced CSS selectors you really may not have heard of — and if you have, you may not have used them

A. M. Douglas
5 min readMar 12, 2016

--

This article is written as an unsolicited continuation of this article purporting to document a number of “advanced CSS selectors” which the reader “has never heard of”.

While attribute/partial selectors can be considered quite “advanced”, the author forgot to mention something important: that they are supported even as far back as Internet Explorer 7. It’s not really fair to say that no-one has heard of these in particular, since they’ve been around for so long, are used by so many people and projects. The primary reason for their apparent obscurity today is the fact that, compared to selecting by class, tag or id, it’s rather inefficient. Nevertheless, some column-based grid systems still use them.

The other selectors mentioned are, in my opinion, quite well-known albeit largely useless.

:not(x) has been discussed on the net for a long time, but it’s really only useful for distinguishing between :hover, :active and :focus states on input fields. Ultimately :not(x) promotes the writing of bad CSS, because you should use specificity and inheritance, rather than negation.

Similarly, :first-of-type and :last-of-type are also not really indicative of well-planned CSS. It would be useful if it would select the first instance of an element within all parent elements, but it rather works like element.querySelector in JavaScript: it selects only the first instance of a selector globally.

If you need to target something so specifically and not in multiple cases, you should probably just give it specific CSS class or an id. If you intend to select the first element in multiple instances — say, in a row when using a grid system — use :first-child and :last-child.

Some really underused CSS pseudo-selectors

You might disagree with my statements about those selectors above, maybe you find them super useful; I personally rarely (if ever) use :not(x), :first-of-type, :last-of-type, et cetera. I think these are some more exciting pseudo-selectors that deserve some attention.

:valid/:invalid

The aforementioned article includes a careless list of extremely well-known pseudo-selectors like :hover, :active, :focus and :visited — while JavaScript is still used by some to handle basic hover effects (yes, even in 2016), these pseudo-selectors are more or less common knowledge. Even :checked sees some usage, especially in Codepens, or in production when native checkbox/radio inputs are replaced with custom graphics.

Something that seems more obscure in the wild are the :valid/:invalid selectors. Their obscurity probably stems from the fact that there’s not much point delegating responsibility for form validation feedback to CSS. JavaScript will almost always be relied upon to detect invalid form entries in a production-ready site anyway, and it seems only natural to let JavaScript act upon invalid entries and indicate state while you’re at it.

This might seem unnecessary to those of you who know the power of HTML5’s new attributes for input fields, like the introduction of the pattern attribute — but not everybody uses a browser which supports HTML5 pattern matching on input fields, and form validation is definitely one of those things that cannot be considered a progressive enhancement. It should be a core feature supported for all. JavaScript is thus the preferable means of validating forms on the client-side.

Of course, JavaScript itself, while it is arguably crucial to the modern web, can be switched off by the user. What should one do in those cases when you still want to communicate state to your users? Well, in an ideal world where everybody uses a modern web browser, the :valid/:invalid pseudo-selectors make it very easy to provide feedback on forms without adding/removing classes programmatically. You can use :valid/:invalid in combination with required and pattern attributes, and thus remove the requirement for JavaScript when providing visual feedback on input validity.

Your code might look like this:

<style>
input { background: hsla(0, 0%, 30%, 1) }
input:enabled { background: hsla(0, 0%, 95%, 1) }
input:enabled:valid { background: hsla(88, 50%, 53%, 0.5) }
input:enabled:invalid { background: hsla(4, 90%, 58%, 0.5) }
</style>
<input type="email" pattern=".{3,}" required>

:target

There’s only really one advanced pseudo-selector that is barely known even among veterans of web development — the :target pseudo-selector. It has creeped into the occasional example, but it’s largely unheard of.

This is probably because it seems largely useless: the first hit on Google involves an article split into hash-linked sections. When the user navigates to a hash-link, :target is then used to apply some indicative styles to the section that the reader has jumped, for the sake of increased user-friendliness. (This example can be found here.)

Yet :target is in fact far more powerful than all that. You can mimic the functionality of a lightbox by using the :target selector. To do this, you might start with some basic code like this:

CSS:

*,:before,:after
{
box-sizing: border-box;
margin: 0;
padding: 0;
border: 0
}
nav
{
display: table;
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
width: 100%;
}
nav > a
{
display: table-cell;
font-size: 21px;
line-height: 1.5;
text-decoration: none;
background-color: white;
color: crimson;
}
img
{
display: none
}
:target img
{
display: block;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 100%;
max-width: 400px;
height: auto;
z-index: 2;
}
:target:after
{
content: '';
display: block;
position: fixed;
top: 0;
left: 0;
bottom: 0;
right: 0;
width: 100%;
height: 100%;
z-index: 1;
background-color: hsla(0, 0%, 0%, 0.6);
}

HTML:

<!DOCTYPE html>
<html>
<nav>
<a href="#i">Figure I</a>
<a href="#ii">Figure II</a>
</nav>
<figure id="i">
<img src="http://i.imgur.com/anS0VlU.jpg">
</figure>
<figure id="ii">
<img src="http://i.imgur.com/aKN6vdJ.jpg">
</figure>
</html>

A better example can be found here, courtesy of MDN.

You can build tabs or even carousels with the same principles. Here is an example of a no-JavaScript-required carousel I made earlier.

Epilogue

These pseudo-selectors are largely useless given the complexity of real-world apps and the fact that JavaScript enables all of this functionality to be governed from one place. Delegating some things to CSS and others to JavaScript produces less maintainable code.

However, if you’ve just got a random project on the table, or even if you want to build a slideshow and you were considering something like impress.js or reveal.js, I’d give :target some consideration.

If you’ve got a trivial form on your website, or if you’d like a fallback method for showing validation states on your input fields, consider :valid/:invalid. Your actual validation should be carried out server-side anyway, because client-side validation can and will be circumvented, no matter if its JavaScript or native HTML5.

--

--