:nth-child(-n+3)匹配父元素前三个子节点(无论类型),而:nth-of-type(-n+3)仅匹配同标签的前三个兄弟元素;DOM中注释、换行文本节点等干扰会导致失效,纯CSS难以可靠实现“先过滤再取前N”。

它不是在“算”,而是在“找位置 ≤ 3 的子节点”
:nth-child(-n+3) 的本质不是执行数学运算,而是浏览器按 CSS 规范将其解释为「匹配父元素中第 1、第 2、第 3 个子节点」。这里的 -n+3 是线性公式 an+b(a = -1, b = 3)的写法,规范定义它等价于位置索引 ≤ b 的所有子元素——也就是前三个物理位置,不管类型。
:nth-child(-n+3) 和 :nth-of-type(-n+3) 完全不是一回事
前者数的是父元素下**所有子节点**(<div>、<p>、注释、换行产生的文本节点都算),后者只数**同标签名的兄弟节点**:
-
li:nth-child(-n+3):只命中位置 1–3 上恰好是<li>的元素;中间插一个<div class="ad">,第三个<li>就变成第 4 个子节点,不匹配 -
li:nth-of-type(-n+3):对所有<li>单独编号,稳稳选中前三个<li>,哪怕中间夹着 5 个<hr>或注释
为什么你写了却没生效?大概率是 DOM 结构“不干净”
常见失效场景:
- HTML 换行缩进生成了不可见的文本节点,让第一个真实
<li>实际是:nth-child(2) - 服务端渲染插入了注释(如
<!-- SSR: injected -->),占了位置 1 - JS 动态用
innerHTML替换内容后,DOM 未重排,浏览器仍按旧结构计算序号 - 用了
display: none隐藏干扰项,但它的 DOM 位置没变,:nth-child()照样计入
别赌结构,真要“前三个 .item”就别硬套 -n+3
如果父容器里有广告位、标题、分隔线等不可控节点,纯 CSS 几乎无法可靠实现「先过滤再取前 N」:
立即学习“前端免费学习笔记(深入)”;
-
.item:not([data-ad]):nth-child(-n+3)是错觉——:not()和:nth-child()是并行匹配,不是管道操作 -
.item:nth-of-type(-n+3)只在所有.item都是同一标签(如全是<div>)时才稳定;自定义组件标签(<my-card>)或伪元素不被识别 - 最稳路径:用 JS 筛一遍
document.querySelectorAll('.item').slice(0, 3),加 class 再写样式
真正难的不是写出 -n+3,而是确认当前 DOM 是否真的只有你要的元素排在前三位——不确认这点,写十遍也白搭。


















