::first-letter 与 :first-child 有何不同(伪类与伪元素)

Ste*_*ven 6 css standards w3c css-selectors

从 W3C 标准对伪元素的定义https://www.w3.org/TR/selectors-3/#pseudo-elements

伪元素创建了超出文档语言指定的文档树的抽象。例如,文档语言不提供访问元素内容的第一个字母或第一行的机制。伪元素允许作者参考这些否则无法访问的信息。

(我的重点。)

为什么文档语言允许检测第一个子元素(所以这:first-child是一个 css 伪)而不是第一个字母(所以这::first-letter是一个 css 伪元素)?应该如何理解这种“文档语言”?

本质上,问题是:为什么选择第一个元素与选择第一个字母不同?为什么文档语言可以检索一种而不是另一种?

我不是在问伪类和伪元素之间的一般区别,而是特别询问为什么第一个字母在概念上与第一个子元素不同。其他伪元件更少混淆:即::after::before例如是伪构件是相当明显的,因为它们涉及到未在HTML结构中定义的“空间”。但是第一个字母,因此为什么这样的第一个字母仍然被区别对待的问题。

Bol*_*ock 5

在最典型的 CSS 用例中,文档语言指的是 HTML。文档树(在整个选择器中提到的)是指从标记构造的 DOM 树。

伪元素是基于现有布局生成的东西。也就是说,必须首先基于应用于 DOM 树中的元素的 CSS 来构造和呈现布局。仅靠文档语言、标记无法实现这一点。

::first-letter例如,您会注意到伪元素仅适用于块容器框。在确定某个元素(或其后代)::first-letter是块容器框(唯一可以直接包含内联内容流的框)之前,无法知道该元素(或其后代)是否将具有伪元素,这是通过以下方式确定的: CSS,而不是 HTML。作为一个更具体的例子:

<p>Hello world!
Run Code Online (Sandbox Code Playgroud)

默认情况下,p元素是display: block。这会产生一个块盒子,它是一种块容器盒子。但即便如此,这个默认值也是使用 CSS 实现的。如果您要使用以下 CSS 规则覆盖该默认值:

p {
  display: inline;
}
Run Code Online (Sandbox Code Playgroud)

然后该元素将成为行内框,并且p::first-letter不再对其产生任何影响。

直观上,与块框(或任何其他类型的框)相比,确定内联框的第一个字母似乎仍然微不足道,但是一旦在同一个内联格式化上下文中有多个内联框都与一个交互,事情就会变得非常复杂其他。

::first-line更加明确:不仅在格式化该行之前不可能知道元素文本的第一个格式化行有多长,而且该行的内容和长度也会随着您调整大小和/或重排而改变元素及其内容。

相反,树结构伪类(例如)独立于布局来:first-child匹配 DOM 中的元素。无需渲染任何内容,浏览器就可以立即分辨出哪个元素是其父元素的第一个子元素。您所需要的只是元素的 DOM 树,这意味着您需要的所有信息都可以从文档语言(标记)中检索。例如,无论您应用什么 CSS,以下片段中的第一个子级始终相同:ol

<ol>
  <li>First
  <li>Second
  <li>Third
</ol>
Run Code Online (Sandbox Code Playgroud)

  • @Mr Lister:问题是问为什么 ::first-line 不在 DOM 中 - 答案是 DOM 是标记的反映,而不是渲染的布局。甚至 CSSOM 也没有提供渲染布局的完整抽象。布局是基于两个对象模型渲染的,而不是相反,并且基于此渲染的布局依次生成伪元素。 (2认同)