:not() 不能嵌套且参数仅限简单选择器,多参数写法在 Safari ≤15.4 中完全失效,链式写法 :not(.a):not(.b) 兼容性最佳,性能开销大且易被误读为“或”逻辑实为“且”。

:not() 不能嵌套,嵌套写法直接失效
浏览器根本不会解析 :not(:not(.active)) 或 :not(.foo:not(.bar)) 这类写法 —— 它们被当作无效选择器丢弃,DevTools 里整条规则会灰掉,控制台也不报错。CSS 规范明确禁止 :not() 的参数中出现另一个 :not()、:is()、:where() 或 :nth-child() 等伪类。
合法的参数仅限「简单选择器」:单个 class(.disabled)、id(#header)、属性([data-id])、标签(button)、或单个伪类(:checked、:disabled)。哪怕只是加个空格变成 .menu li,就立刻非法。
-
:not(.hidden)✅ -
:not([type="submit"])✅ -
:not(button:disabled)✅(标签 + 单伪类仍算简单) -
:not(:not(.x))❌ -
:not(.a.b)⚠️ Level 4 合法但 Safari ≤15.4 静默忽略
Safari ≤15.4 不支持多参数 :not(.a, .b)
写成 :not(.a, .b) 在 Chrome 105+ 和 Firefox 103+ 没问题,但在 Safari 15.4 及更早版本中,整条 CSS 规则会被直接跳过,样式完全不生效,且 DevTools 里显示为 “Invalid CSS”。这不是兼容性降级,是彻底不识别。
安全写法永远是链式::not(.a):not(.b)。它在所有现代浏览器(包括 Safari 12+)都稳定可用。
立即学习“前端免费学习笔记(深入)”;
- 想排除带
.error或.hidden的元素 → 用:not(.error):not(.hidden) - 构建时若用 PostCSS,
postcss-preset-env默认不转译多参数:not(),需显式配置notParens: true - 不要指望 autoprefixer 补兼容 ——
:not()没厂商前缀,IE 全系列(6–11)压根不支持
链式 :not() 是“且”关系,不是“或”
a:not(.external):hover 的真实意思是:“是 a 标签” + “没有 .external 类” + “处于 hover 状态”。三个条件必须同时满足。它**不是**“排除 .external 或 hover 中任意一个”,而是全部成立才匹配。
这点常被误读。例如:.list li:not(.first):not(.last) 会把同时有 .first 和 .last 的项也排除掉 —— 因为只要命中任一 :not() 条件,该元素就被筛掉。
- 要排除“同时带 .a 和 .b”的元素,应写
:not(.a.b)(但注意 Safari ≤15.4 兼容性) - 要排除“带 .a 或 .b 中任意一个”的元素,
:not(.a):not(.b)就是正确写法 - 伪类顺序建议把
:not()放前面,如input:not(:disabled):focus,可读性更好,旧引擎兼容性也略优
:not() 的性能和优先级容易被低估
浏览器匹配 ul > li:not(.special) 时,会先选中所有 li,再逐个判断是否「不满足 .special」。DOM 节点越多,否定判断开销越大,尤其在滚动或动画场景下可能拖慢渲染。
优先级方面,:not() 自身不增加权重,但内部选择器参与计算:input:not([type="hidden"]) 的 specificity 是 0-1-1(input 的 0-0-1 + [type] 的 0-1-0),而 input 单独写是 0-0-1 —— 所以前者本该更高,但如果样式没生效,大概率是其他规则覆盖了它,或者目标元素压根没匹配上(比如写了 input[type="hidden"] 却忘了加 type 属性)。
- 避免在高频更新区域(如列表滚动)大量使用
:not() - 调试时优先检查 DOM 是否真含对应 class/attribute,而不是怀疑
:not()逻辑 - 动态框架(如 Vue/React)中,scoped style 可能干扰
:not()匹配,需确认 class 名未被 hash 处理


















