:has(+ .active) 无效因缺少宿主选择器;正确写法如 li:has(+ li.active),表示匹配紧邻 .active 兄弟前的 li 元素,+ 要求紧邻,~ 表示后续任意位置,且存在兼容性与性能限制。

:has(+ .active) 为什么不能直接写成这样
直接写 :has(+ .active) 是无效的——它缺少宿主选择器,语法不完整。:has() 必须依附于某个基础元素(比如 li、div 或通用选择器 *),否则浏览器无法判断“谁在检查自己后面有没有 .active”。你真正想表达的是:“找一个元素,它后面紧跟着一个带 .active 类的兄弟元素”,这个“前面那个元素”才是目标,必须显式写出来。
正确写法:用宿主 + :has(+ .active) 定位前一个兄弟
典型场景是导航菜单中,当某个 li 被激活时,想给它**前面那个** li 加样式(比如加个右分割线)。正确写法是:
li:has(+ li.active) {
border-right: 2px solid #2563eb;
}这里 li 是宿主,:has(+ li.active) 表示“这个 li 后面紧跟一个 li.active”。只有当前项满足该条件,才会命中。
- 不能写成
*:has(+ .active)—— 虽然语法合法,但会匹配所有祖先元素(包括html、body),极易误触发 - 宿主必须是实际存在的、与目标兄弟同级的元素;如果中间隔了
ul或nav,+就失效,得换用~或嵌套:has() -
+ .active中的空格不能省:+.active是非法语法,会被忽略
和 ~ .active 的区别:紧邻 vs 后续任意位置
:has(+ .active) 和 :has(~ .active) 都能实现“前置元素响应”,但行为完全不同:
立即学习“前端免费学习笔记(深入)”;
-
li:has(+ li.active):只匹配**正前方一个**且后面**紧挨着**li.active的li(中间不能有其他兄弟) -
li:has(~ li.active):匹配**前面任意位置**存在li.active的所有li(哪怕中间隔着 5 个其他li) - 若要精准控制“仅前一项”,必须用
+;若只是“前面有就行”,用~更稳妥
例如,在横向导航中,li:has(+ li.active) 可用于给“即将被激活项”的左侧加 hover 边框;而 li:has(~ li.active) 更适合做“已激活项之前的全部项变灰”这类批量状态。
容易忽略的兼容性与性能细节
:has(+ .active) 在 Chrome 105+、Firefox 121+、Safari 15.4+ 均可用,但仍有两个硬限制:
- 不能出现在 CSS @keyframes 或 @container 查询中(会直接报错)
- 不能用于 :is()、:where() 等逻辑组合选择器内部作为子表达式(例如
:is(li:has(+ .active))不合法) - 复杂嵌套的
:has()(如:has(+ :has(.foo)))可能触发重排开销,尤其在长列表中频繁切换状态时,建议用 class 切换兜底
真正难的不是写对语法,而是判断 DOM 结构是否真的支持 + 关系——多一层 wrapper 就全盘失效,这时候就得退回到 :has(~ .active) 或 JS 监听。


















