先记住这个答案
::first-line 只对块级容器元素的第一行渲染文本生效。这里说的「第一行」不是源码里的第一行,而是浏览器实际渲染出来的第一行。窗口宽度变了,第一行的内容也会跟着变。行内元素、置换元素(如 <img>)以及 flex 和 grid 容器上写 ::first-line 不会有任何效果。
- 只对块级容器的首行文本生效
margin、padding等盒模型属性不可用- 首行是动态的,随容器宽度变化
- flex 和 grid 容器上不生效
哪些属性能用在 first-line 上
::first-line 不是什么属性都能写。规范只允许一部分属性,主要是和文字外观相关的。
/* 这些可以用 */
p::first-line {
color: #333;
font-weight: bold;
font-size: 1.2em;
background-color: lightyellow;
text-decoration: underline;
letter-spacing: 0.05em;
text-transform: uppercase;
}
/* 这些不能用,写了也没效果 */
/* margin, padding, border, display, width, height */能用的主要包括:color、font 系列、background 系列、text-decoration、text-transform、letter-spacing、word-spacing、line-height。盒模型相关的属性如 margin、padding、border、width、height 全部不行。
这个限制的原因是,「第一行」是浏览器在排版阶段才确定的,它不是一个独立的盒子,没办法给它单独设置边距或宽度。
第一行是动态的
::first-line 最容易被误解的地方就是它是动态的。同一段文字,在宽屏上第一行可能有 50 个字,在窄屏上可能只有 20 个字。浏览器会在每次排版时重新计算哪些文字属于第一行。
.article p::first-line {
font-size: 1.3em;
color: darkblue;
}这段代码让文章段落的第一行加大加粗。试着拖动浏览器窗口改变宽度,会看到深蓝色的文字范围在实时变化。放大字号后文字会折到下一行,深蓝色也跟着缩短。
这和 ::first-letter 不同。::first-letter 始终是第一个字符,不会因为换行而变化。
在 flex 和 grid 容器上不生效
如果一个元素设置了 display: flex 或 display: grid,它就不再是传统的块级容器,::first-line 对它不生效。即使它里面的子元素有文字,::first-line 也不会命中。
这是因为 flex 和 grid 容器不产生「行盒」的概念。它们的子元素各自独立排列,没有连续的文本流,也就没有「第一行」可言。如果需要给 flex 容器里的文字加样式,应该把 ::first-line 写在具体的子元素上,而不是容器上。
容易答错的地方
- 以为 first-line 是源码的第一行
::first-line指的是浏览器渲染后的第一行,不是 HTML 源码里的第一行。一段写在一行里的长文字,渲染时被折成了三行,::first-line只会影响折行后的第一行。- 以为 first-line 可以设置边距
margin、padding、border写在::first-line上不会有任何效果,也不会报错。这是规范的限制,因为第一行不是独立的盒子。
面试官还会怎么问?
first-line 和 first-letter 有什么区别?
::first-letter 只选中第一个字符(或标点加第一个字符),范围是固定的。::first-line 选中渲染后的整个第一行,范围随容器宽度变化。两者能用的属性也不同,::first-letter 可以设置 margin 和 padding,::first-line 不行。
first-line 的优先级怎么算?
::first-line 在层叠中比较特殊。虽然它用的是 :: 伪元素语法,但浏览器把它当成一个独立的虚拟元素来参与层叠计算。如果元素本身的选择器和 ::first-line 设置了同一个属性,::first-line 的样式会优先应用到第一行文本上。
行内元素里嵌套块级元素时,first-line 选的是哪一行?
::first-line 只对最外层的块级容器生效。如果块级容器的第一个子元素是另一个块级元素,那 ::first-line 会穿透进去,选中那个嵌套块级元素的第一行文本。但如果第一个子元素是行内元素,就直接选那行文字。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。