用 li + li 替代 li:not(:first-child) 更可靠,因其只匹配紧邻前一个 li 的元素,不依赖父容器子元素类型,避免注释、空格等干扰;若坚持用 :not(:first-child),须限定为 ul > li:not(:first-child) 并注意 ie8 不兼容。

直接说结论:用 :not(:first-child) 给列表项加分割线是可行的,但必须配合正确的选择器结构和边界条件——否则容易漏掉首项却多画一条线,或在动态内容中失效。
为什么 :not(:first-child) 不能直接套在 li 上加 border-top
常见错误写法:li:not(:first-child) { border-top: 1px solid #ccc; }。表面看是“除第一个都加顶边框”,但实际会出问题:如果列表里有其他元素(比如 li 前插了 div 或注释节点),:first-child 判定的是父容器下的第一个子元素,不一定是第一个 li。此时 :first-child 可能匹配不到任何 li,导致所有 li 都被选中。
- ✅ 正确前提:父容器只包含
li,且无空白文本节点(如换行缩进) - ⚠️ 动态渲染时(React/Vue),服务端注入或 JS 插入的节点可能破坏这个前提
- ? 更稳妥的写法是用
li + li(相邻兄弟选择器),它只关心“前面紧挨着一个li”的元素,不依赖 DOM 位置绝对性
li + li 比 :not(:first-child) 更可靠的实际场景
当你要给每个非首项的 li 加上顶部分割线,li + li 是更少意外的选择。它天然排除第一个 li,且不关心父容器里有没有其他类型子节点。
ul {
list-style: none;
padding: 0;
}
li {
padding: 8px 0;
}
li + li {
border-top: 1px solid #eee;
}
- ✅ 即使
ul开头有注释、空格或script标签,li + li依然只作用于第二个及之后的li - ✅ 支持伪元素替代方案(如用
::before画线),避免边框影响盒模型高度 - ❌ 不适用于需要“底部边框”的场景(比如想给非末项加
border-bottom),这时得换思路,比如用li:not(:last-child)
用 :not(:first-child) 的正确姿势与兼容性注意点
如果你坚持用 :not(:first-child)(比如要统一管理多个不同标签的排除逻辑),必须确保选择器明确限定上下文。
- ✅ 推荐写法:
ul > li:not(:first-child)—— 使用子选择器>锁定直接子元素,减少意外匹配 - ✅ 若列表可能含空
li,加:not(:empty)过滤:ul > li:not(:first-child):not(:empty) - ⚠️ IE8 不支持
:not(),若需兼容,老项目建议回退到类名控制(如 JS 添加is-not-first) - ⚠️
:first-child匹配的是“类型无关”的第一个子元素;如果第一个子是li,那没问题;但如果第一个是li前还有span,那第一个li就不是:first-child,:not(:first-child)就会误判
真正容易被忽略的是:分割线是否参与布局高度。用 border-top 会撑开父容器(尤其当 li 用了 box-sizing: content-box),而用 margin-top 或伪元素则更可控。别只盯着选择器,先想清楚“这条线到底该占空间,还是仅作视觉分隔”。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











