:has()无法替代~实现多级树形菜单,因其仅能检测存在性而不能精确控制层级关系;必须用唯一ID配合:checked ~ .submenu兄弟选择器逐级控制。

:has() 不能用于构建多级联动树,它不支持跨层级状态传递或递归控制。所谓“联动树”,需要父节点状态影响子节点样式,而 :has() 只能向上匹配祖先、向内匹配后代,无法让一个 input:checked 同时驱动自身兄弟 .submenu 和该 .submenu 内部的下一级 input 状态 —— 它没有“状态广播”能力。
为什么 :has(input:checked) 无法替代 ~ 兄弟选择器
常见误解是用 .menu:has(input:checked) .submenu 替代传统 input:checked ~ .submenu,但这两者语义完全不同:
-
input:checked ~ .submenu是“当前 checkbox 被选中时,展开它后面同级的 .submenu”——精确、可控、可嵌套 -
.menu:has(input:checked) .submenu是“只要 .menu 下任意一个 input 被选中,就把所有 .submenu(包括深层嵌套的)一起展开”——无粒度、易误触、破坏独立折叠 - 如果某一级
input未被点击,但其父级.menu内其他input被选中,它的.submenu也会意外展开 -
:has()不区分“谁被选中”,只回答“有没有”,无法表达“这个 input 控制这个 submenu”
多级树必须每层配独立 input,且结构严格线性
真正可行的纯 CSS 多级树依赖 DOM 顺序和兄弟选择器,:has() 在这里没有插入点:
-
input必须是.submenu的**前驱兄弟节点**,不能包裹、不能嵌套、不能跨容器 - 每一级折叠都需唯一
id,例如node-1控制第一级.submenu,node-1-2控制第二级,不可复用 - 正确结构:
<input type="checkbox" id="node-1"><br><label for="node-1">一级</label><br><ul class="submenu"><br> <input type="checkbox" id="node-1-2"><br> <label for="node-1-2">二级</label><br> <ul class="submenu"></ul><br></ul>
- 对应规则只能是:
#node-1:checked ~ .submenu和#node-1-2:checked ~ .submenu,而非任何:has()表达式
:has() 在树形菜单里唯一安全的用途
它只能做“存在性反馈”,不能做“状态控制”:
立即学习“前端免费学习笔记(深入)”;
- 检测某级是否有任意项被选中,然后高亮整个分组:
.group:has(input:checked) { background: #f0f8ff; } - 切换父级 label 的图标:
label:has(input:checked) .icon { opacity: 1; },前提是.icon在label内部 - 显示“已选 X 项”提示:
.group:has(input:checked) .counter { display: block; },但.counter必须与input同属一个父容器 - 切勿尝试用
:has()驱动max-height动画或display切换——它不响应状态变化,且 Safari 对复杂:has()的重绘延迟明显
真正难的是让每一级收放互不干扰,且在 iOS 上 touch 不跳级。:has() 看似灵活,实则把“谁控制谁”的契约交给了 DOM 结构模糊性,反而增加调试成本。老老实实用 ~ + 唯一 ID + max-height,才是目前最稳的路径。


















