:has(+ .sibling) 匹配的是紧邻 .sibling 前一个兄弟元素,要求 DOM 中严格相邻、中间无任何节点(包括空白文本、注释、隐藏元素等);常见失效源于实际 HTML 结构与预期不符,需通过 JS 特性检测降级并优先采用更鲁棒的父容器方案。

:has(+ .sibling) 不是“向前选兄弟”,而是“从候选元素出发,检查它后面是否紧跟着 .sibling”——换句话说,它匹配的是 .sibling 的前一个相邻兄弟,但必须满足 DOM 结构上**严格紧邻**,中间不能有任何节点干扰。
为什么 .box:has(+ .circle) 有时完全不生效
常见失效不是语法写错,而是 HTML 实际结构和开发者预期不符。浏览器按真实 DOM 树匹配,不是按视觉排列或缩进格式。
- DOM 中存在
display: none元素(哪怕不可见)仍算兄弟节点,会打断+关系 - React/Vue SSR 注入的 hydration 注释(如
<!-- react-mount-point-unstable -->)是真实文本节点,+ .circle直接失效 -
innerHTML = '<div class="box"></div>\n<div class="circle"></div>'中的换行符会生成空白文本节点,破坏相邻性 -
<div class="box"></div> <span></span><div class="circle"></div>—— 中间有<span></span>,.box:has(+ .circle)不匹配
:has(+ .sibling) 能否选前第 n 个兄弟
可以,但靠链式 +,不是靠 ~ 或索引。链越长,可读性和性能损耗越明显,建议仅用于前 1–2 个。
- 选
.circle前第一个.box:.box:has(+ .circle) - 选前第二个:
.box:has(+ .box + .circle)(即:当前.box→ 紧跟另一个.box→ 那个.box后紧跟.circle) - 选前第三个:
.box:has(+ .box + .box + .circle) -
.box:has(+ * + .circle)在绝大多数浏览器中不被支持,*在:has()内部受限严重
兼容性与降级必须手动兜底
:has() 在 Chrome 105+、Firefox 121+、Safari 15.4+ 支持,但 Edge 105+ 之后才稳定,旧版 iOS Safari(≤15.3)和所有 IE 完全不支持。不能依赖 @supports 自动降级,因为 @supports selector(:has(...)) 本身在不支持的浏览器里就解析失败。
立即学习“前端免费学习笔记(深入)”;
- 生产环境必须用 JS 特性检测:
if (CSS.supports('selector(:has(*))')) { /* 加载 :has() CSS */ } - 对关键交互(如表单验证高亮),优先采用父容器锚点方案:
.form-group:has(> .error),比.input:has(+ .error)更鲁棒 - 链式
:has(+ .a + .b + .c)在低端设备上可能触发重排延迟,动态频繁插入/移除节点时需实测渲染帧率
真正难的不是写出 :has(+ .sibling),而是确认你控制的 HTML 输出里,两个元素之间**真的什么都没有**——没有空格、没有注释、没有隐藏节点、没有框架注入的占位符。这点在 SSR 或组件化渲染中极易被忽略。



















