firefox 120及更早版本完全不支持:has(),解析器直接报错;121+才默认启用且需layout.css.has-selector.enabled为true,生产环境必须js降级。

Firefox 120 及更早版本根本不支持 :has(),不是“不生效”,而是解析器直接报错或跳过整条规则;Firefox 121+ 才默认启用,且必须确保 layout.css.has-selector.enabled 为 true。
确认 Firefox 版本和底层开关是否开启
旧版 Firefox(如 120 或更早)压根没实现 :has() 语法,document.querySelector('div:has(.child)') 会直接抛出 DOMException: "is not a valid selector"。即使你看到 CanIUse 显示“部分支持”,也别信——它可能只指实验性标志可手动开启,而非生产可用。
- 打开
about:config,搜索layout.css.has-selector.enabled,双击设为true - 同时检查 Firefox 版本:菜单 → 帮助 → 关于 Firefox,必须 ≥ 121.0;120 及以下即使开标志,
querySelectorAll仍可能异常或匹配失败 - 刷新页面后,在 DevTools 的 Styles 面板里找对应规则——没出现 = 浏览器未识别;出现了但被划掉 = 被更高权重样式覆盖
别依赖 CSS.supports('selector(:has(*))') 做降级判断
这个检测在 Firefox 120 中返回 false 是对的,但在某些旧 Chromium 版本中会返回 true 却完全不渲染样式,属于假阳性。Firefox 本身虽较诚实,但你不能靠它决定是否加载 JS fallback——因为检测通过 ≠ 样式真生效。
- 真正可靠的验证只有两个条件同时满足:
CSS.supports('selector(:has(div))')返回true,且目标父元素在 DevTools 的 Computed 面板中确实应用了该规则的声明 - 不要用
@supports selector(:has())包裹关键样式块,构建工具(如 cssnano)可能直接删掉整个@supports块,连降级样式都丢 - 如果你在 Angular 或其他框架中写
.form:has(input:invalid),先确认 input 有required或type="email"等原生校验属性——纯 JS 校验不会触发:invalid,:has()就永远匹配不到
Firefox 生产环境必须 JS 降级,不能赌用户开实验标志
用户几乎不会、也不该去 about:config 开 layout.css.has-selector.enabled。你在本地开了,不等于客户开了;CI/CD 构建时用了 cssnano,默认配置会直接删掉所有含 :has() 的规则——上线后样式就没了。
- 监听精准事件代替全量监听:
input用blur或change,details用toggle,避免 MutationObserver 的高开销和误触发 - 父元素必须有稳定标识:比如
class="form-group"或id="login-form",别用div:nth-child(2)这种结构脆弱的选择器,JS 找不到就永远加不上类 - 安全判定示例:
form:has(input:invalid)→Array.from(el.querySelectorAll('input')).some(i => !i.checkValidity());注意checkValidity()不会自动弹提示,如需提示得额外调i.reportValidity()
最易被忽略的是:哪怕 Firefox 版本够、标志开了,:has() 在动态插入节点(比如图片懒加载后补 .error 类)或 Shadow DOM 内部时仍可能失效——它不穿透影子边界,也不保证与 JS 状态同步。别只测静态 HTML,得在真实交互流里看 class 是否及时更新、样式是否跟上。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











