nth-child按父元素所有子元素顺序计数(从1开始),含标签、文本、注释节点;nth-of-type仅按同类型标签计数;n代入从0开始,但序号从1起算。

nth-child 选不中元素,大概率是索引算错了
它不是按“你在 HTML 里写的第几个 <div>”来数,而是按“父元素下所有同级子元素的顺序”来数,且从 1 开始。哪怕中间夹着 <p>、<span> 或注释节点,都会被计入索引。
常见现象:div:nth-child(2) 没生效,但你明明写了两个 <div> —— 很可能第一个是 <h2>,第二个才是 <div>,那这个 <div> 实际是第 2 个子元素,但它的索引是 2,所以能命中;但如果它是第 3 个子元素(前面有 <h2> 和 <p>),div:nth-child(2) 就完全匹配不到。
nth-child(n) 和 nth-of-type(n) 的关键区别
nth-child 看位置,nth-of-type 看标签类型。这是最常混淆的点。
-
div:nth-child(2):父元素的第 2 个子元素必须是<div>,否则不匹配 -
div:nth-of-type(2):父元素下所有<div>中的第 2 个,不管它前面有多少其他标签
示例结构:
立即学习“前端免费学习笔记(深入)”;
<section> <h2>标题</h2> <p>说明</p> <div>第一个 div</div> <div>第二个 div</div> </section>
此时:
– div:nth-child(3) ✅ 匹配第一个 <div>(它是第 3 个子元素)
– div:nth-child(4) ✅ 匹配第二个 <div>
– div:nth-of-type(2) ✅ 同样匹配第二个 <div>,但写法更稳定、语义更准
伪类里的 n 是从 0 还是 1 开始?
n 始终从 0 开始代入计算,但最终匹配的是「结果序号」,而序号本身从 1 开始计数。比如 :nth-child(2n) 展开为 2×0=0(无效)、2×1=2(第 2 个)、2×2=4(第 4 个)…… 所以它选的是偶数位子元素。
容易踩的坑:
-
:nth-child(0)永远不匹配(没有第 0 个元素) -
:nth-child(-n+3)匹配前 3 个子元素(n=0→3,n=1→2,n=2→1,n=3→0 无效) - 写
:nth-child(1)和:first-child效果一样,但前者多一次计算,后者语义清晰且性能略优
用开发者工具快速验证 nth-child 是否生效
别靠猜。在 Chrome DevTools 的 Elements 面板中:
- 右键目标元素 → “Break on” → “attribute modifications” 不适用,改用直接看 Styles 面板右侧是否列出该选择器
- 更可靠的做法:在 Console 中运行
document.querySelector('div:nth-child(2)'),看返回值是不是你要的元素 - 如果返回
null,用Array.from(el.parentNode.children).map((c, i) => [i+1, c.nodeName])打印出所有子元素的序号和标签名,一眼看清索引分布
复杂嵌套或动态插入内容时,DOM 实际结构往往和模板文件不一致——注释、空格文本节点、服务端渲染残留、JS 插入的占位符,都可能悄悄改变子元素顺序。


















