能用,但必须满足三个硬条件:浏览器支持(Chrome 105+ / Firefox 121+ / Safari 16.4+)、:has() 必须写在父容器选择器上、规则顺序要按“从多到少”叠加,否则样式不生效或被覆盖。

能用,但必须满足三个硬条件:浏览器支持(Chrome 105+ / Firefox 121+ / Safari 16.4+)、:has() 必须写在父容器选择器上、规则顺序要按“从多到少”叠加,否则样式不生效或被覆盖。
检测“至少 n 个子元素”该用 :has(*:nth-child(n)) 而不是 :has(:nth-child(n))
很多人写 .container:has(:nth-child(3)),结果 3 个子项时没反应——因为 :nth-child(3) 要求目标元素自身是第 3 个子元素,而 .container 不可能是自己的子元素。真正想表达的是“是否存在第 3 个子元素”,得用 *:nth-child(3) 这种通配写法。
-
.container:has(*:nth-child(4))→ 至少 4 个子项(即 ≥4) -
.container:has(*:nth-child(1):nth-last-child(1))→ 恰好 1 个(第一个也是最后一个) -
.container:has(*:nth-child(2):nth-last-child(2))→ 恰好 2 个
注意::nth-last-child() 计数只认元素节点,DOM 中的换行、空格、注释都会干扰结果,建议子项用 display: contents 或确保结构干净。
实现阶梯式列数(1–3→1列,4–6→2列,7+→3列)必须按顺序写三组规则
CSS 没有 > 或 < 运算符,只能靠组合 :has() 和 :not(:has()) 逼近范围。关键不是逻辑对不对,而是层叠顺序——规则必须从“最多”往“最少”排,否则低优先级会覆盖高优先级:
立即学习“前端免费学习笔记(深入)”;
-
.grid:has(> div:nth-child(7))→ ≥7 个,设repeat(3, 1fr) -
.grid:has(> div:nth-child(4)):not(:has(> div:nth-child(7)))→ ≥4 且 <7,即 4–6 个,设repeat(2, 1fr) -
.grid:not(:has(> div:nth-child(4)))→ <4 个,设1fr
漏掉中间的 :not(:has(> div:nth-child(7))),那 7+ 个子项也会命中第二条规则,最终列数错乱。
:has() 在 Safari 16.4 之前完全不支持,降级方案不能只靠 @supports
Safari 15.x 及更早版本会静默忽略整个含 :has() 的规则,连 fallback 都不会触发。所以 @supports not selector(:has()) 只能识别语法是否被解析,不能保证行为一致。
- 最稳的落地方式:预设
.count-1、.count-2、.count-3等 class,由 JS 注入(比如监听MutationObserver) - 纯 CSS 替代:用
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)),虽不能精确控数量,但视觉上常够用 - 别在
@media或@supports里嵌套:has(),部分旧版 Safari 不支持嵌套中的:has()
真正难的不是写出语法正确的规则,而是意识到 :has() 是一次性 DOM 快照判断——它不监听变化,也不处理文本节点,更不兼容老浏览器。一旦项目需覆盖 iOS 15 或 macOS Monterey 之前的用户,就得提前规划 JS fallback 路径。


















