Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How is ::first-letter different from :first-child (pseudo-class vs pseudo-element)

From the W3C standard's definition of pseudo-elements https://www.w3.org/TR/selectors-3/#pseudo-elements:

Pseudo-elements create abstractions about the document tree beyond those specified by the document language. For instance, document languages do not offer mechanisms to access the first letter or first line of an element’s content. Pseudo-elements allow authors to refer to this otherwise inaccessible information.

(My emphasis.)

Why excatly does the document language allow for detection of the first child element (so that :first-child is a css pseudo-class) but not of the first letter (so that ::first-letter is a css pseudo-element)? How should this "document language" be understood?

In essence the question is: Why is there a difference in picking the first element in contrast to the first letter? Why can the document language retrieve one but not the other?

I am not asking to the general difference between pseudo classes and pseudo elements, but rather I am asking specifically about why a first letter is conceptually different from a first child element. Other pseudo elements are less confusing: that ::after and ::before for example are pseudo elements is fairly obvious, as they relate to a "space" that isn't defined in the html structure. But the first letter is, so therefore the question of why such first letter is still treated differently.

like image 968
Steeven Avatar asked Feb 26 '19 14:02

Steeven


1 Answers

The document language refers, in the most typical CSS use case, to HTML. The document tree (as referred to throughout Selectors) refers to the DOM tree that is constructed from the markup.

A pseudo-element is something that is generated based on an existing layout. That is, a layout must first be constructed and rendered based on CSS that's applied to elements in a DOM tree. This cannot be accomplished with the document language, the markup, alone.

You'll notice that the ::first-letter pseudo-element only applies to block container boxes, for example. There's no way to know whether an element (or its descendant) will have a ::first-letter pseudo-element until it's determined to be a block container box (the only kind of box that can directly contain a flow of inline content), and that is determined by CSS, not by HTML. As a more concrete example:

<p>Hello world!

By default, a p element is display: block. This results in a block box, which is a kind of block container box. But even so, this default is implemented using CSS. And if you were to override that default with the following CSS rule:

p {
  display: inline;
}

this element then becomes an inline box, and p::first-letter will no longer have any effect on it.

Intuitively, it still seems trivial to determine the first letter of an inline box compared to a block box (or any other type of box), but things get pretty complicated once you have several inline boxes in the same inline formatting context all interacting with one another.

::first-line is much more clear-cut: not only is it impossible to know how long the first formatted line of an element's text is until you format the line, but the contents and length of this line can also change as you resize and/or reflow the element and its contents.

In contrast, a tree-structural pseudo-class such as :first-child matches elements in the DOM independently of the layout. Without having to render anything, the browser can tell right off the bat which of the elements is the first child of its parent. All you need is the DOM tree of elements, which means all the information you need can be retrieved from the document language, the markup. For example, the first child of the ol in the following fragment is always the same no matter what CSS you apply to it:

<ol>
  <li>First
  <li>Second
  <li>Third
</ol>
like image 140
BoltClock Avatar answered Oct 21 '22 21:10

BoltClock