:first-of-type选中的是父元素内同标签类型中的首个元素,而非第一个出现的该标签;它忽略文本节点和注释,但受其位置影响,且不支持按class等条件筛选首个匹配项。

为什么 :first-of-type 有时不选中你想要的第一个元素
它不是“父容器里第一个出现的这个标签”,而是“父容器里所有同类型标签中的第一个”。比如父元素里有 <div>、<p>、<div>,那么第二个 <div> 不会被 div:first-of-type 选中——因为第一个 <div> 才是该类型的首个。
常见错误现象:ul li:first-of-type 在 <ul> 开头有注释或文本节点时失效;或者列表项前插了 <li class="header">,结果想高亮的“第一条数据”没被命中。
- 它只看标签名(
li、div),不看 class、id 或属性 - 文本节点、注释、
<!-- -->不参与类型计数,但会挤占位置,影响后续兄弟元素的“同类型序号” - 若需排除特定
<li>(如带role="presentation"的分隔符),:first-of-type无能为力,得换思路
:first-of-type 和 :nth-of-type(1) 有区别吗
没有实质区别。两者在规范和浏览器实现中完全等价,:nth-of-type(1) 就是 :first-of-type 的函数式写法。
使用场景上,:first-of-type 更简洁直白;但如果你后续要扩展成 :nth-of-type(2n) 或需要动态计算,统一用 :nth-of-type() 系列更利于维护。
立即学习“前端免费学习笔记(深入)”;
- 不要混用:同一选择器里写
li:first-of-type:nth-of-type(1)是冗余且可能触发意外重绘 - 注意兼容性:
:nth-of-type在 IE9+ 支持,:first-of-type同样从 IE9 开始支持,旧项目需确认底线版本 - 性能无差异,现代浏览器对两者优化程度一致
想选“第一个带 class 的 <li>”,不能只靠 :first-of-type
li.foo:first-of-type 的含义是:既是 foo 类,又是所有 <li> 中的第一个——它不会跳过前面不带 foo 的 <li> 去找“第一个带 foo 的”。这是最常被误解的点。
例如:<li>intro</li><li class="foo">one</li><li class="foo">two</li>,此时 li.foo:first-of-type 不会匹配任何元素,因为第一个 <li> 没有 foo 类。
- 真正需要的是“同级中第一个满足条件的元素”,CSS 本身不提供这类逻辑,得靠 JS 或重构 DOM 结构
- 可行替代:给目标元素加唯一
data-属性(如data-first-foo),再用li[data-first-foo] - 或用 JS 定位:
el.querySelector('li.foo')(返回第一个匹配)比依赖 CSS 伪类更可控
和 :first-child 混用时容易踩的坑
:first-child 要求元素必须是父元素的第一个子节点,而 :first-of-type 只要求是该类型中第一个。两者在纯标签序列中结果相同,但只要中间穿插其他标签,就立刻分道扬镳。
典型翻车现场:表格行 <tr> 前加了 <tr class="summary">,你想高亮首条数据行,用 tr.data-row:first-child 失效,换成 tr.data-row:first-of-type 才对——但前提是 tr.data-row 确实是所有 <tr> 中第一个该类型。
- 检查 DOM 结构是否干净:用浏览器开发者工具的“Elements”面板直接看子节点顺序,别只信 HTML 源码缩进
- 服务端渲染或框架(如 React)可能注入空文本节点或注释,让
:first-child失效,:first-of-type相对更鲁棒 - 如果父容器只有一种标签(如全是
<li>),优先用:first-child,语义更明确,也略快一点点(引擎少做一次类型过滤)
事情说清了就结束。真正在意“第一个符合条件的元素”时,CSS 伪类的能力边界比直觉窄得多——别硬扛,该加属性加属性,该交由 JS 处理就交出去。










