:has(+ .active)匹配的是紧邻.active元素前一个兄弟元素,而非所有前置元素;它要求HTML为扁平兄弟结构,且受空格、浏览器兼容性及伪元素限制。

:has(+ .active) 不能实现“前向选择”,它实际匹配的是“前面那个元素”——即紧邻的前一个兄弟元素,不是它前面的所有元素,也不是它自身之前的任意祖先。 这个写法常被误解为“向前找”,但 CSS 中没有真正意义上的前向(preceding)选择器;:has() 的 + 是紧邻兄弟(next sibling)关系符,方向是“从当前元素出发,往后看有没有匹配的兄弟”,而 :has(+ .active) 的宿主必须是这个“前面的元素”本身。
为什么 :has(+ .active) 匹配的是“前一个”,而不是“当前元素之前所有”
关键在 :has() 的语义:它永远作用于“自身”,括号里描述的是“从它出发能向下/向兄弟找到什么”。+ 表示“下一个紧邻兄弟”,所以 .item:has(+ .active) 的意思是:“选中所有后面紧跟一个 .active 兄弟的 .item 元素”——也就是那个 .active 元素的**前一个兄弟**。
-
.item:has(+ .active)→ 匹配的是.active前面那个.item -
.item:has(~ .active)→ 匹配的是.active前面**任意位置**的.item(只要中间隔着零个或多个兄弟) - 不存在
:has(- .active)或:has(– .active)这类语法;CSS 不支持反向兄弟选择
:has(+ .active) 的典型使用场景和 HTML 结构要求
它只在兄弟元素严格相邻、且状态由后置元素驱动时才有效。比如导航菜单中高亮“上一项”,或步骤条中强调“已完成的前一步”。
- HTML 必须是扁平兄弟结构:
<div class="step">1</div><div class="step">2</div><div class="step active">3</div> - 对应 CSS:
.step:has(+ .active) { opacity: 0.7; }→ 只会让“2”变灰,不会影响“1” - 如果中间插了其他元素(如
<hr>或注释),+就断开,匹配失败 - 不适用于嵌套结构(如
<li><span>A</span></li><li class="active">B</li>),因为<span>不是<li>的兄弟
容易踩的坑:空格、浏览器兼容性与伪类组合限制
:has(+ .active) 看似简单,但实际落地时几个细节会直接导致样式不生效:
立即学习“前端免费学习笔记(深入)”;
- 括号内必须有空格:
:has(+ .active)✅,:has(+.active)❌(部分浏览器解析失败) - Firefox 121+ 才默认启用
:has(),旧版需检查是否开启layout.css.has-selector.enabled - 不能和伪元素连用:
::before:has(+ .active)无效,:has()不接受伪元素作为宿主 - 不能用于
@keyframes或transition属性值中,仅用于选择器主体 -
.active必须真实存在于 DOM 中且未被display: none完全移出流(visibility: hidden不影响匹配)
真正需要“前向影响多个祖先”的场景,:has(+ .active) 无能为力;这时候得换思路——要么用 :has(~ .active) 覆盖更广范围,要么把状态逻辑上提到共同父容器,用 parent:has(.active) 统一控制后代。别硬套 +,方向感错了,样式就永远追不上需求。


















