:first-child 失效因目标元素非父容器首个子节点,如前有注释、空格或其它标签;应优先用 li:first-of-type 或添加 class 处理结构不确定性。

为什么 :first-child 有时不生效
因为 :first-child 要求目标元素必须是其父容器的**第一个子节点**,且类型要匹配。常见失效场景:父元素开头有注释、空格文本节点,或第一个子元素是 <div> 而你写的是 li:first-child。
- HTML 中换行缩进产生的空白文本节点,会让
<li>实际变成第二个子节点 -
ul > li:first-child不会选中<ul>下第一个<li>,如果它前面有<div>或注释 - 想忽略非
<li>元素?用:first-of-type更稳妥
:first-of-type 和 :first-child 怎么选
看你要匹配的是“位置优先”还是“类型优先”。:first-of-type 只关心同类型元素中的第一个,不care前面有没有别的标签。
- 适用场景:
ul里混写了<li>和<div class="divider">,仍想给第一个<li>加样式 → 用li:first-of-type -
:first-child更严格,适合结构绝对干净的列表(比如 SSR 渲染无空格、无注释) - 性能上没区别,但可维护性:当 HTML 结构可能变动时,
:first-of-type更鲁棒
在真实项目里怎么写才不容易翻车
别只靠伪类硬扛结构不确定性。加一层明确的 class 是最省心的做法。
- 后端或模板层渲染时,直接给第一个
<li>加class="first",CSS 写li.first { ... } - 用 JS 动态添加(如 React 中
items.map((item, i) => <li key={i} className={i === 0 ? 'first' : ''}>...</li>)) - 如果必须用伪类,优先测试实际 DOM 结构:
console.dir(document.querySelector('ul').childNodes)看前几个是不是Text节点
兼容性与现代替代方案
:first-child 和 :first-of-type 在 IE9+ 都支持,不用操心老浏览器。但注意:CSS 选择器层级太深或滥用伪类可能触发重排,尤其在长列表中频繁切换状态时。
立即学习“前端免费学习笔记(深入)”;
- 避免写成
div ul li:first-child a span:hover这种超长链,拆成更具体的 class 更高效 - CSS Nesting(如
&:first-child)在 PostCSS 或 Sass 中可用,但原生 CSS 还不支持嵌套伪类 - 真正复杂的条件逻辑(比如“第一个可见的”“第一个 data-status=active 的”),伪类搞不定,得靠 JS


















