.item:first-of-type 并非选所有 .item 中第一个,而是选父容器中同标签类型(如div)的第一个元素且恰好带 .item 类;真正选首个 .item 应用 .item:not(.item ~ .item)。

为什么 .item:first-of-type 不是你想选的“第一个 .item”
.item:first-of-type 的匹配逻辑是:先找父元素中**第一个 div(或对应标签)**,再看它有没有 item 类。它根本不管其他 div.item 在哪儿、有多少个。
常见错误场景:<div class="wrapper">
<p>intro</p>
<div class="item">A</div>
<div class="item">B</div>
</div> —— 这里 .item:first-of-type 会失效,因为第一个 div 是 <div class="wrapper">,而它没有 <code>item 类;后续两个 .item 虽然都是 div,但已不是“同类型中的第一个”。
- 它不扫描所有带
.item的元素再取第一个 - 它不按 class 分组,只按 HTML 标签名分组(
div、p、span等) - 如果父元素下第一个
div没有.item,整条规则就静默不匹配
替代方案:用 :not(.item ~ .item) 真正选“第一个 .item”
这是目前最可靠、无需 JS、纯 CSS 的解法,语义明确:「有 .item 类,且后面没有另一个 .item 兄弟」——即它是最后一个,反过来推,前面那个没被任何 .item 压住的,就是第一个。
写法:.item:not(.item ~ .item)
- ✅ 匹配目标:父元素中 DOM 顺序上第一个出现的
.item - ✅ 不依赖标签名,
div.item、li.item、section.item都能用 - ⚠️ 注意:必须确保所有目标元素是同一层级的兄弟节点,不能跨嵌套层级
- ⚠️ IE9+ 支持,现代项目可放心用;IE8 及以下需 JS 回退
什么时候该放弃伪类,改用 JS 或语义类名
当结构复杂、动态插入频繁,或需要兼容 IE11 及更早版本时,CSS 伪类反而增加维护成本。
- JS 方案:在渲染后执行
document.querySelector('.item')或el.classList.add('item--first'),再写.item--first样式 - 语义类名方案:服务端或构建时直接输出
<div class="item item--first">,样式写 <code>.item--first - 不要用
:nth-child(1)替代——它数的是所有子节点位置,不是.item的序号;.item:nth-child(1)要求目标必须是父元素第一个子节点,条件太苛刻 - 父元素实际包含注释节点或空格文本节点,导致结构和预期不符(用 DevTools 的 Elements 面板展开看子节点列表)
- CSS 规则被更高优先级样式覆盖,而
.item:not(.item ~ .item)这种组合选择器优先级不高,容易被#container .item盖掉 - Shadow DOM 或 CSS-in-JS 作用域限制:若目标元素在 Shadow Root 内,外部样式表默认不生效,需确认样式是否注入到正确 scope
调试时最容易忽略的三个点
即使写了正确的选择器,也常因这些细节失败:











