:has() 是当前唯一一种无需 JavaScript、也不用额外添加 class,就能让父级容器根据是否包含 img 标签自动切换样式的纯 CSS 解决方案。不过它只能检测 DOM 结构中是否存在 标签,无法判断图片是否真正加载成功;而 :has(img:loaded) 目前也还没有任何浏览器支持。

:has() 可以直接让父容器(例如 .card)根据内部是否存在 img 元素来切换布局样式。这也是目前唯一一种不依赖 JS、无需增加额外 class,就能实现“有图片时使用 flex,无图片时使用 block”的纯 CSS 写法。
为什么不能用 :has(img:loaded) 判断图片是否加载成功
:has(img:loaded) 目前仍属于草案阶段的伪类选择器。截止 2026 年 8 月,Chrome、Firefox 以及 Safari 都尚未支持它——只要把它写进样式表,整条 CSS 规则就会直接失效。现阶段真正可以稳定使用的,只有 :has(img):它判断的只是 DOM 中有没有 标签,至于 src 是否为空、图片地址是否 404,或者资源是否还在加载中,它都不会进一步判断。也就是说:
.card:has(img)→ 只要 HTML 中存在,即使src=""或图片路径错误也会匹配.card:not(:has(img))→ DOM 结构中根本没有出现标签- 如果想区分“图片加载失败”,仍然需要借助 JS 监听
error事件并追加 class(如.img-error),然后再配合.card:has(.img-error)编写样式
:has() 和传统 class 切换方案的实际差异
在不使用 :has() 的情况下,通常需要手动维护两套 class,比如 .card 和 .card-no-image,同时还要让后端或模板引擎先判断是否有图,再输出对应的 class。使用 :has() 之后,HTML 结构可以保持静态不变,CSS 会根据内容结构自动响应变化:
- 更适合服务端渲染(SSR)场景:卡片内容来自 API,但模板层不必再写额外的 if-else 判断
- 减少 class 冲突或遗漏风险:例如某张卡片忘记加
-no-image,就容易导致页面样式异常 - 降级逻辑更清晰:当浏览器不支持
:has()时,所有卡片都会使用默认样式,因此可以提前设置好 fallback
示例对比:
.card { display: block; border-top: 3px solid #7c93e9; }
.card:has(img) { display: flex; align-items: center; gap: 1rem; }
搭配响应式布局时的常见坑
:has() 本身并不会感知屏幕尺寸变化,但它可以与媒体查询配合使用——需要特别注意写法顺序与浏览器兼容性:
- 正确写法:
@media (min-width: 768px) { .card:has(img) { flex-direction: row; } } - 错误写法:
.card:has(img) @media (min-width: 768px) { ... }—— 这是非法语法,浏览器会直接忽略整条规则 - 移动端要谨慎同时使用
aspect-ratio和:has():部分旧版 Safari 对aspect-ratio的支持较弱,如果卡片必须保持固定宽高比,建议使用 padding-top 百分比技巧作为 fallback - 性能方面通常没有明显压力:现代浏览器已经对
:has()做了优化,但仍应避免在层级极深的嵌套结构中连续使用多层判断,例如div:has(div:has(div:has(...)))
很多开发者最容易忽略的一点,其实是 DOM 结构必须尽量保持稳定。:has(img) 匹配的是当前时刻真实存在的节点;如果通过 JS 动态插入或删除 ,对应样式也会实时更新。但如果直接使用 innerHTML 整体替换整张卡片内容,原有的 img 节点会被销毁,新的节点又需要重新参与渲染,这种情况下就可能短暂出现布局闪动或样式错位。
