li:last-of-type比li:last-child更稳,因其只匹配父容器中最后一个li标签,无视注释、空格、其他标签等干扰节点;而li:last-child要求目标必须是父元素的最后一个子节点,结构敏感易失效。

li:last-of-type 为什么比 li:last-child 更稳?
因为 :last-of-type 只看同标签元素的 DOM 顺序,不care后面有没有注释、空格文本节点、<p></p> 或 <div>。只要它是父容器里最后一个 <code>li,就命中——这点在 CMS 输出、JS 动态插入、模板拼接等场景中特别关键。
常见失效现场:<ul>
<li>A</li>
<li>B</li>
<li>C</li>
</ul>
<!-- footer -->,此时 li:last-child 完全不匹配(最后一个子节点是注释),但 li:last-of-type 仍能选中 C。
注意::last-of-type 不识别 class 或属性,li.active:last-of-type 和 li:last-of-type 效果一样——它只认 li 这个标签名。
清除下边框的标准写法
核心逻辑是「先统设,再覆盖」,不是「单独给末项设 none」:
-
li { border-bottom: 1px solid #ddd; }—— 所有项默认带底边框 -
li:last-of-type { border-bottom: none; }—— 最后一个li覆盖为无边框
别漏掉盒模型影响:如果 li 有 padding 或 margin,清除边框后视觉间距可能突变,建议同步检查 padding-bottom 是否需要调整。
兼容性放心:Chrome 4+、Firefox 3.5+、Safari 3.2+、Edge 12+ 全支持;IE8 及以下不支持,如需兼容,得 fallback 到 JS 添加 class 或服务端标记。
什么时候不该用 :last-of-type?
当列表里混用了不同标签,且你只想排除特定 class 的末项时,:last-of-type 就不够用了。比如:
- Item 1
- Item 2
这时 li:last-of-type 仍会选中 Item 2,但它前面那个 class="divider" 其实不该参与“内容项”计数。
可行替代方案:
- 改用
li:not(.divider):last-of-type(现代浏览器支持) - 结构上把 divider 提到
<ul></ul>外,或用::after伪元素画分隔线 - 服务端/JS 控制,给真正末项加
is-lastclass
更现代的替代思路:用 gap 替代边框
如果目标只是「让列表项之间有间隔、末尾不留多余线」,直接放弃边框 + :last-of-type 组合,改用布局级控制更干净:
ul { display: flex; flex-direction: column; gap: 12px; }- 完全不需要
border-bottom,也没有伪类匹配风险 - gap 自动处理末项无额外间距,且支持响应式缩放(比如
gap: clamp(8px, 3vw, 16px))
兼容性已不是问题:Chrome 89+、Firefox 63+、Safari 14.1+、Edge 90+ 均原生支持。唯一要注意的是,flex column 会破坏某些依赖 block 流的样式(比如 text-align: justify),上线前务必实测。
真正容易被忽略的点::last-of-type 匹配的是“所有同类型兄弟中的最后一个”,不是“当前 DOM 片段里渲染出来的最后一个”。动态插入新 li 后,旧的末项会自动失去样式——这既是优势也是陷阱,得确认业务是否允许这种响应式行为。











