:has()支持Chrome 105+、Firefox 121+、Safari 15.4+,但Safari 15.4–16.3对兄弟选择器支持不稳,Firefox 120需手动启用;不可用于@media/@keyframes;仅支持可匹配真实节点的选择器,禁用:hover/:focus/::before等;性能敏感,宜用于局部静态结构。

现在可以直接用 :has() 控制父级样式,不用 JS 监听、不改 HTML 结构、不加额外 class —— 但前提是浏览器够新,且写法不能出错。
哪些浏览器能用 :has() 而不降级失效
Chrome 105+、Firefox 121+、Safari 15.4+ 已默认支持,2026 年可视为“稳定可用”。但要注意:
- Safari 15.4–16.3 对
:has()内部含+或~的兄弟选择器支持不稳定,比如.card:has(.trigger + .content)可能被忽略 - Firefox 120 及更早版本需手动开启
layout.css.has-selector.enabled标志,生产环境不能依赖 - 所有版本都不支持在
@media或@keyframes中嵌套:has() - 如果规则无效(比如用了不支持的伪类),整条 CSS 规则会被浏览器静默丢弃,DevTools 里也看不到匹配痕迹
:has() 里能写什么、不能写什么
括号内必须是“能实际匹配到 DOM 节点”的选择器,不是所有伪类都行:
- ✅ 支持:
input:checked、[data-status="error"]、img[src*="icon"]、button:disabled、:valid、:invalid、:placeholder-shown - ❌ 不支持:
:hover、:focus、:focus-visible(状态动态且不可预测)、::before(伪元素无真实节点) - ⚠️ 小心:
:empty在:has()里永远不匹配 —— 因为:has()自身的存在已让父元素“非空”;用:not(:placeholder-shown)替代:not(:empty)更可靠 - ⚠️ 避免宽泛写法:
div:has(*)或section:has(*:hover)会触发频繁重计算,尤其在滚动或悬停密集区域
表单校验场景下最稳的写法
这是目前兼容性最好、语义最清晰的落地场景,例如高亮整个输入项容器:
立即学习“前端免费学习笔记(深入)”;
label:has(input:not(:placeholder-shown):valid) {
border: 2px solid #28a745;
padding: 8px;
}
.form-group:has(input:invalid) .error-msg {
display: block;
}
-
input:not(:placeholder-shown)比input[value!=""]可靠 —— 后者读的是初始value属性,不随用户输入更新 - 加上
:valid可过滤掉 placeholder 消失但内容仍非法的情况(如邮箱格式错误) - 不要用
input:focus做条件,焦点切换快,样式可能闪动或不同步 - 若用
required,建议组合:valid,否则空值提交后也会触发:invalid,但用户还没输过
为什么 .parent:has(.child) 不能反过来写
语法强制要求 :has() 必须挂在祖先选择器后面,顺序不可逆:
- ✅ 正确:
nav:has(.active)、.card:has(img:hover)(注意:后者仅 Chrome 支持 hover,Safari 不认) - ❌ 错误:
.active:has(…)、img:hover:has(…)—— 浏览器直接忽略整条规则,控制台无报错 - ❌ 错误:
div:has(.child):hover——:hover必须写在:has()外层,否则解析失败 - 关键点:
:has()是一个“条件过滤器”,它修饰的是前面那个选择器,不是独立存在的选择器
真正容易被忽略的,是 :has() 的性能边界:它需要反向遍历 DOM 树。当父容器很深、子元素频繁增删(比如聊天列表实时追加消息),或规则写在 body 级别时,样式重计算开销会上升。别把它当万能钩子,优先用于局部、静态结构明确的模块。


















