:first-child 失效是因为它只匹配父元素的第一个子节点,不区分类型;若前面有注释、文本节点或其它标签,则不匹配。应改用 :first-of-type 或添加 class 控制。

为什么 :first-child 有时不生效
它只认“是不是父元素的第一个子节点”,不看类型。比如 <div><p>A</p><span>B</span></div> 中,<p> 是第一个子元素,p:first-child 能命中;但若前面加个 <!-- 注释 --> 或 <div><em>X</em><p>A</p></div>,<p> 就不是第一个子节点了,:first-child 直接失效。
常见错误现象:li:first-child 没加粗、button:first-child 没变色——大概率是 DOM 结构里它前面有别的元素(空格换行在某些解析模式下也算文本节点)。
- 检查浏览器开发者工具的 Elements 面板,确认目标元素是否真为父容器的 第一个子节点
- 如果父容器开头有注释、文本节点或其它标签,
:first-child就不可靠 - 想按“同类型中的第一个”匹配,得换
:first-of-type
:first-of-type 和 :first-child 的关键区别
:first-of-type 看的是“同名标签中排第一的那个”,不管中间隔了什么。比如 <div><em>1</em><p>2</p><span>3</span><p>4</p></div>,p:first-of-type 会选中 <p>2</p>,而 p:first-child 根本不匹配(因为 <p> 不是第一个子节点)。
-
:first-child→ “我是老大(位置第一)” -
:first-of-type→ “我是同类里最早出生的那个(类型内序号第一)” - 性能上没差别,但语义完全不同;误用会导致样式完全不出现
- IE9+ 支持
:first-of-type,如果要兼容 IE8 及更早,只能靠 JS 或加 class
真正“同级中第一个某类元素”的稳妥写法
如果目标是“在一组兄弟元素中,选第一个 button”,且不确定 DOM 是否干净,最稳的方式是:明确加 class 控制,而不是赌伪类。
立即学习“前端免费学习笔记(深入)”;
比如:<div><span>hint</span><button class="first-action">Submit</button><button>Cancel</button></div>,然后写 .first-action 样式。
- 伪类依赖结构,class 依赖意图——后者更可控
- 构建时加 class 成本极低,维护时不会因模板微调就崩样式
- 如果必须用 CSS 选择器,且确定只有
button一种可交互元素,可用button:not(:nth-of-type(2))这种绕路写法,但可读性差,不推荐
Flex/Grid 容器里 :first-child 还管用吗
管用,但容易被误解。Flex 或 Grid 不改变 DOM 树结构,只改变渲染顺序。:first-child 仍按 HTML 顺序判断,和视觉顺序无关。
比如:<div style="display: flex; flex-direction: row-reverse"><div>A</div><div>B</div></div>,视觉上 B 在左、A 在右,但 A 还是第一个子元素,div:first-child 依然匹配 A。
- 想按视觉顺序选“最左边那个”,不能靠
:first-child,得结合order属性或改 HTML 顺序 - Grid 中若用
grid-template-areas重排,同样不影响:first-child的匹配逻辑 - 这点最容易在响应式切换 display 类型时翻车
CSS 选择器不操作 DOM,只观察结构。所谓“同级第一个”,本质是你对“同级”和“第一个”的定义是否和浏览器解析结果一致——多数时候问题不出在写法,而出在没打开开发者工具看一眼真实节点顺序。


















