从W3C标准的伪元素https://www.w3.org/TR/selectors-3/#pseudo-elements的定义:
伪元素创建关于文档树的抽象,超出文档语言指定的抽象。例如,文档语言不提供访问元素内容的第一个字母或第一行的机制。伪元素允许作者参考这个否则无法访问的信息。
(我的重点。)
为什么文档语言允许检测第一个子元素(以便:first-child
是一个css伪类)而不是第一个字母(因此::first-letter
是一个css伪元素)?如何理解这种“文档语言”?
从本质上讲,问题是:为什么选择第一个元素与第一个字母形成鲜明对比?为什么文档语言可以检索一个而不是另一个?
我并没有要求伪类和伪元素之间的一般区别,而是我特别询问为什么第一个字母在概念上与第一个子元素不同。其他伪元素不那么混乱:例如::after
和::before
是伪元素是相当明显的,因为它们与未在html结构中定义的“空间”相关。但是,第一个字母是,因此,为什么这样的第一封信仍然被区别对待的问题。
在最典型的CSS用例中,文档语言指的是HTML。文档树(在整个选择器中引用)是指从标记构造的DOM树。
伪元素是基于现有布局生成的。也就是说,必须首先根据应用于DOM树中元素的CSS构造和呈现布局。单独使用文档语言(标记)无法实现这一点。
例如,您会注意到::first-letter
伪元素仅适用于块容器盒。没有办法知道一个元素(或它的后代)是否会有一个::first-letter
伪元素,直到它被确定为一个块容器盒(唯一一种可以直接包含内联内容流的盒子),并且这是确定的通过CSS,而不是HTML。作为一个更具体的例子:
<p>Hello world!
默认情况下,p
元素是display: block
。这导致了一个块盒,它是一种块容器盒。但即便如此,这个默认值是使用CSS实现的。如果您使用以下CSS规则覆盖该默认值:
p {
display: inline;
}
这个元素然后变成一个内联框,p::first-letter
将不再对它产生任何影响。
直观地说,与块框(或任何其他类型的框)相比,确定内联框的第一个字母似乎仍然微不足道,但是当在同一个内联格式化上下文中有多个内联框时,一切都变得相当复杂另一个。
::first-line
更加清晰:在格式化线条之前,不仅无法知道元素文本的第一个格式化行的长度,而且在调整大小和/或重排时,此行的内容和长度也会发生变化元素及其内容。
相比之下,像:first-child
这样的树结构伪类与DOM中的元素无关,而与布局无关。无需渲染任何内容,浏览器可以直接告诉其中哪些元素是其父元素的第一个子元素。您所需要的只是元素的DOM树,这意味着您可以从文档语言(标记)中检索所需的所有信息。例如,无论您应用什么CSS,以下片段中ol
的第一个子节点总是相同的:
<ol>
<li>First
<li>Second
<li>Third
</ol>