首页 > 解决方案 > ::first-letter 与 :first-child 有何不同(伪类与伪元素)

问题描述

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

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

(我的重点。)

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

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

我不是问伪类和伪元素之间的一般区别,而是我特别问为什么第一个字母在概念上与第一个子元素不同。::after其他伪元素不那么令人困惑:::before例如伪元素是相当明显的,因为它们与 html 结构中未定义的“空间”相关。但是第一个字母,所以为什么这样的第一个字母仍然被区别对待的问题。

标签: csscss-selectorsstandardsw3c

解决方案


在最典型的 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>

推荐阅读