In my post about 5 lesser used CSS selectors, I used a single colon when referring to the first-letter and first-line pseudo-elements. It was pointed out to me that I should have used the double colon notation for selectors because they were pseudo-elements.

So, in this article, I explore the difference between pseudo-elements and pseudo-classes and which colon syntax should be used.

Pseudo-Classes vs Pseudo-Elements

Pseudo-classes and pseudo-elements are types of css selectors that allow us to select elements which are not normally available through the DOM. There are currently only 4 psuedo-elements, and 23 pseudo-classes -

Pseudo-Classes Pseudo-Elements
E:root, E:nth-child(n), E:nth-last-child(n), E:nth-of-type(n), E:nth-last-of-type(n), E:first-child, E:last-child, E:first-of-type, E:last-of-type, E:only-child, E:only-of-type, E:empty, E:link, E:visited, E:active, E:hover, E:focus, E:target, E:lang(x), E:enabled, E:disabled, E:checked, E:not(s) E::first-line, E::first-letter, E::before, E::after

Pseudo-classes

Pseudo-classes, according to the W3C Recommendation, provide a way for us to select elements "based on information that lies outside of the document tree or that cannot be expressed using the other simple selectors".

This covers two cases. First, the selection of information based on information that lies outside the document tree. An example of this is the :visited pseudo-class.

a:visited {  
  // styles
}

Use of this pseudo-class is dependent on information about the link that is not accessible through the DOM. There is no way, in HTML or JavaScript, to determine if a link has been visited.

The second case is the selection of elements that cannot be expressed using other simple selectors. An example of this is :nth-child(n).

.list-item:nth-child(6) {
  // styles
}

Although we may have the class name of the the specific element we want, there is no other way to select an item based on its position amongst its siblings.

Psuedo-classes select elements that already exist in the DOM, but which are under conditions that are not accessible through the DOM or through other simple selectors

Pseudo-elements

Pseudo-elements, on the other hand, "create abstractions about the document tree beyond those specified by the document language".

In other words, using pseudo-element selectors essentially creates a new virtual element that can be manipulated as though it were its own element. These new virtual elements do not exist independently, but rather belong to another existing element, called their originating element.

For example, with the ::after pseudo-element, we can generate new content that does not actually exist in the DOM. Using the example I gave two weeks ago -

*:lang(de)::before {
  content: "DE";
  position: absolute;
  top: 0;
  left: -55px;
  width: 40px;
  height: 40px;
  text-align: center;
  padding: 10px 0;
  border-radius: 50%;
  background-color: #FFCE00;
}

With the ::first-line and ::first-letter selectors, although the content does already exist, using the selector allows us to target the content in a way that is not normally accessible and treat the content as though it were its own element.

article::first-letter {  
  font-size: 8rem;
  line-height: 1;
  float: left;
  padding-right: 1rem;            
  margin-top: -2rem;
}

Pseudo-elements create a virtual element which lies outside the document tree, either with content that already exists, or by creating new content entirely.

 

The difference between pseudo-classes and pseudo-elements can be summarised as -

With pseudo-classes, it is the information used to the select the element that lies outside the document tree. With pseudo-elements, it is the element itself that lies outside the document tree.

Colon Notation: Single or Double?

In CSS2, both pseudo-classes and pseudo-elements used single colon notation. In CSS3, to make a distinction between the two types of selectors, it was introduced that pseudo-elements use a double colon instead.

foo:pseudo-class  
bar::pseudo-element  

In the Editor's Draft for CSS4 selectors, it is specifically stated -

"Authors must always use the double-colon syntax for these pseudo-elements"

However, the problem is that Internet Explorer 8, which according to caniuse.com currently accounts for 2.19% of global browser usage, does not support the double colon notation. It only accepts a single colon for both pseudo-classes and pseudo-elements.

Because of this, all modern browsers are required to accept the single colon syntax for the current 4 pseudo-elements mentioned (but not for any future pseudo-elements introduced).

So which version should we be using? At this point, and depending on our target audience, IE 8 usage is large enough that we may still need to support it and therefore use the single colon syntax. However, the default should be to use the double colons wherever possible for future proofing.