li:not(:last-child)不生效是因为:last-child匹配的是dom树中最后一个子节点,而html注释、空白文本节点、包裹容器或order重排等干扰源会使最后一个li无法被识别为:last-child;应改用li:not(:last-of-type)或链式:not()排除多条件。

直接用 li:not(:last-child) 就行,但必须确保 DOM 结构干净——否则最后一项仍会被加下划线。
为什么 li:not(:last-child) 有时不生效?
不是选择器写错了,而是浏览器根本没把最后一个 li 当作 :last-child。常见干扰源:
- ul 标签末尾有 HTML 注释(如
<!-- end -->),导致:last-child匹配到注释节点,而非li - li 前后存在空格、换行产生的文本节点(哪怕肉眼不可见)
- li 被包裹在额外的
div或span中,破坏了同级兄弟关系 - 使用了 Flex/Grid 的
order属性,但:last-child只看原始 DOM 顺序,不感知视觉重排
更鲁棒的替代方案:优先用 :last-of-type
当结构不可控时,:last-of-type 更可靠——它只认标签类型,无视文本节点和注释:
li:not(:last-of-type) {
text-decoration: underline;
}
注意::last-of-type 在纯 li 列表中效果等同 :last-child,但语义更稳;若父容器混排了 p、div 等,就得确认是否真要排除“所有 li 中的最后一个”。
需要同时排除多个条件怎么办?
比如既要跳过最后一项,又要跳过带 .no-underline 类的项——不能靠逗号分隔,而要用链式 :not():
li:not(:last-of-type):not(.no-underline) {
text-decoration: underline;
}
这表示“既不是最后一个 li,又没有 .no-underline 类”的项才会被选中。如果你本意是“只要满足任一条件就排除”,那得改用 JS 或重构结构——CSS 的 :not() 天然是 AND 逻辑,不支持 OR。
真正容易被忽略的点是:下划线是否来自继承或父级样式。检查 computed styles 时,重点看 text-decoration 是直接声明的,还是从 a、nav 等祖先元素继承来的——伪类无法覆盖继承值,必须显式设为 none 或用更高优先级规则重置。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











