在编写列表、表格这类重复结构的页面时,经常需要对某个位置的元素单独设置样式,比如让表格偶数行变色、让列表前三项加粗。如果手动为每个元素添加类名,代码会变得冗长且难以维护。CSS提供的结构性伪类选择器nth-child正好解决这个问题,它根据元素在父容器中所有子元素里的排列位置来匹配节点,不需要任何额外的HTML标记。本文将从基本语法讲起,逐步深入到公式参数、常见误区以及实际项目中的典型用法。

一、nth-child的基本语法与匹配规则
nth-child的基本写法是在伪类后面跟一个参数,参数可以是数字、关键字或者表达式。最简单的形式是:nth-child(2),表示选中父元素中的第二个子元素。需要注意的关键点是:它匹配的前提是元素必须是父元素的子节点,并且位置完全对应。也就是说,li:nth-child(2)选中的是父元素下排在第二位的那个子元素,同时这个子元素必须是li标签,如果第二位是其他标签,则不会命中任何元素。
除了具体数字,nth-child还支持两个关键字:odd和even。odd匹配所有奇数位置的子元素,也就是第1、3、5个;even匹配偶数位置,即第2、4、6个。这两个关键字在实现表格斑马纹时使用频率最高,一行代码就能完成隔行变色的效果。
/* 选中第2个子元素 */
li:nth-child(2) {
color: red;
}
/* 奇数行背景色 */
tr:nth-child(odd) {
background-color: #f5f5f5;
}
/* 偶数行背景色 */
tr:nth-child(even) {
background-color: #fff;
}
参数位置还支持an+b形式的表达式,其中a和b都是整数,n从0开始递增。浏览器会依次代入n的值计算结果,结果落在有效索引范围内(从1开始)的都会被选中。例如:nth-child(2n)等价于even,:nth-child(2n+1)等价于odd,而:nth-child(3n)表示选中第3、6、9个元素,:nth-child(n+4)表示从第4个开始往后全部选中。理解这个表达式的运算逻辑是用好nth-child的核心。
二、nth-child与nth-of-type的区别
初学者最容易混淆的就是nth-child和nth-of-type。两者的核心区别在于计数方式:nth-child统计的是父元素下的所有子元素,不区分标签类型;而nth-of-type只统计同种标签类型的兄弟元素。举例来说,如果一个ul里混排了li和p标签,那么li:nth-child(2)要求第二个子元素恰好是li,否则匹配失败;而li:nth-of-type(2)则是在所有li中找第二个,不管li前面排了多少个其他标签。
<ul> <p>提示文字</p> <li>第一项</li> <li>第二项</li> <li>第三项</li> </ul>
针对上面这段HTML,li:nth-child(2)什么也选不中,因为排在第二位的是第一个li,而它是父元素的第三个子节点。如果写成li:nth-child(3)才能选中第一项li。但如果换成li:nth-of-type(2),选中的就是第二个li元素。这个差异在实际开发中经常导致样式不生效,当发现nth-child选不到预期的元素时,第一反应应该是检查父容器中是否混入了其他类型的标签。
与这两个选择器配套的还有first-child、last-child、only-child以及nth-last-child等。first-child等价于nth-child(1),nth-last-child(1)则从末尾开始计数,用来选中倒数第一个元素。当需要选中倒数第几个或者除了前几个以外的所有元素时,nth-last-child配合表达式非常方便,比如:nth-last-child(-n+3)可以选中最后三个元素。
三、典型应用场景与实用技巧
第一个经典场景是表格斑马纹。数据表格行数较多时,隔行变色能显著提升可读性,传统做法需要在服务端输出行号并判断奇偶,或者用JavaScript遍历添加类名,而CSS一行规则即可搞定,且窗口尺寸变化、行数增减时都能自动适应。
/* 表格斑马纹,同时悬停高亮 */
tbody tr:nth-child(odd) {
background-color: #fafafa;
}
tbody tr:nth-child(even) {
background-color: #ffffff;
}
tbody tr:hover {
background-color: #e8f4ff;
}
第二个常见需求是列表分组处理。比如一个导航列表,想让前三个菜单突出显示、后面的菜单弱化,可以这样组合使用:li:nth-child(-n+3)选中前三个,li:nth-child(n+4)选中第四个开始的所有项。再比如时间轴或步骤条组件,需要给每个阶段的图标设置不同颜色,可以用:nth-child(3n+1)、:nth-child(3n+2)、:nth-child(3n)把所有元素按三组循环分配样式,形成周期性的视觉节奏。
第三个技巧是在栅格布局中处理边界元素。使用flex或grid做卡片列表时,每行最后一个元素往往需要去掉右边距,这时候如果没有gap属性可用,可以借助nth-child公式。假设每行放四个卡片,写.card:nth-child(4n)即可命中每行末尾的卡片,将其margin-right设为0。这种做法比给末尾元素手动加类名可靠得多,因为数据条数变化时样式依然正确。
/* 四列布局,每行最后一个卡片去掉右边距 */
.card {
width: calc(25% - 15px);
margin-right: 20px;
margin-bottom: 20px;
}
.card:nth-child(4n) {
margin-right: 0;
}
需要注意的是,nth-child的性能开销比普通类选择器略高,因为它需要计算兄弟节点的位置关系,在超大规模的DOM中大量嵌套使用可能带来轻微的渲染成本,不过常规页面上这种差异几乎可以忽略。另外,nth-child的选择器特异性(specificity)与类选择器相同,多个规则冲突时按书写顺序和特异性综合判断,排查样式覆盖问题时可以借助开发者工具查看具体是哪条规则生效。
掌握nth-child的关键在于记住两点:它按所有子元素的位置计数,参数支持an+b表达式。在实际项目中,优先使用语义清晰的关键字和简单公式,遇到匹配异常时先用开发者工具检查DOM结构中是否混入了意外标签,或者改用nth-of-type按类型计数。熟练运用之后,你会发现大量原本需要JavaScript才能完成的位置相关样式需求,用纯CSS就能优雅地解决。