:first-child不生效是因为它只匹配父元素下第一个且类型完全相同的子节点;若首节点是注释、空格或其它标签,则目标元素即使为同类首个也不会被选中。

为什么 :first-child 有时不生效?
因为 :first-child 只匹配父元素下**第一个子节点**,且必须和选择器的元素类型完全一致。如果父元素第一个子节点是 <p>,你写 div:first-child 就不会命中任何 <div>——哪怕它确实是第一个 <div>。
常见错误场景:
- HTML 中有注释、文本节点(比如换行空格)或
<script>标签在最前面,导致目标元素不是真正的“第一个子节点” - 用在动态渲染的列表中,但服务端或 JS 插入了隐藏元素(如
<template>或<span style="display:none">)
<ul> <!-- 这个注释让下面的 li 不再是 first-child --> <li>Item 1</li> <li>Item 2</li> </ul>
此时 li:first-child 不会匹配任何元素。
:first-of-type 看的是“同类中的第一个”
:first-of-type 不关心位置顺序,只看当前元素类型在父容器中**第几次出现**。只要它是该类型(比如 <div>)第一次出现,就匹配,不管前面有没有 <p>、<span> 或注释。
立即学习“前端免费学习笔记(深入)”;
适用场景:
- 需要样式化每种类型首个元素(例如文章里第一个
<h2>、第一个<figure>) - HTML 结构不可控,但语义类型明确(CMS 输出、富文本编辑器内容)
<article> <p>Intro text</p> <h2>Section title</h2> <p>More text</p> <h2>Another title</h2> </article>
h2:first-of-type 会命中第一个 <h2>,而 h2:first-child 则不会——因为 <h2> 前面还有 <p>。
嵌套结构里两者的差异更明显
当父元素内有多个层级、混合标签时,容易误判“第一个”的归属。关键是:两者都只作用于**直接子元素**,不跨层。
示例结构:
<section>
<div class="wrapper">
<p>First p</p>
</div>
<p>Second p</p>
</section>-
p:first-child→ 不匹配任何<p>(第一个子是<div>) -
p:first-of-type→ 匹配第二个<p>(它是父元素下第一个<p>类型) -
.wrapper:first-child→ 匹配<div class="wrapper">(它是第一个子) -
.wrapper:first-of-type→ 同样匹配(它是唯一的<div>,也是第一个)
注意::first-child 和 :first-of-type 都不识别 class 或属性,只基于 HTML 元素标签名和 DOM 树位置判断。
兼容性与现代替代建议
两者在 IE9+ 和所有现代浏览器中都支持,无兼容风险。但要注意:
- 它们无法表达“第一个带某 class 的元素”——这种需求得用 JS 或预处理器模拟
- CSS Selectors Level 4 提案中新增了
:nth-child(1 of <selector>),但目前仅 Safari 支持,不能用于生产 - 若逻辑复杂(比如“第一个可见的
<li>”),CSS 无解,必须靠 JS 控制 class
真正容易被忽略的是:伪类匹配发生在渲染前,不响应 DOM 动态变化(如 display: none 切换不会触发重选)。所以别指望它替代 JS 的状态管理。










