【:has()】 CSS关系性伪类介绍及实战

目录

[第一章 前言](#第一章 前言)

[第二章 :has() 语法速记](#第二章 :has() 语法速记)

[第三章 实用场景](#第三章 实用场景)

[第四章 性能 & 兼容性](#第四章 性能 & 兼容性)

[第五章 总结](#第五章 总结)


第一章 前言

小编最近刚好遇到了两个样式的需求,如下:

需求1:(原因:是element-ui弹窗的层级低于嵌入ifram第三方ui的内置层级)

但是element-ui有以下两情况,有时class="v-modal"蒙层会在class="el-dialog__wrapper login-dialog"上面,有时会在下面,而且小编发现项目中使用到的所有element的弹窗组件都会有class="v-modal",那么就不能直接修改class="v-modal"的z-index层级(会影响到全局的),但是只修改class="el-dialog__wrapper login-dialog"又不会生效;

解决方案:小编添加了login-dialog类名,然后想通过login-dialog类名去获取class="v-modal",如痴操作的就相当于针对性的只修改了一处的样式了;但是问题来了,两个样式如何关联上呢?此时小编想到了:has!

处理如下:

css 复制代码
// v-modal后面有login-dialog的兄弟节点改v-modal样式
.v-modal:has(~ .login-dialog) {
    z-index: 9999 !important;
}

// v-modal是login-dialog的下一个兄弟节点改v-modal样式
.login-dialog + .v-modal {
    z-index: 9999 !important;
}

需求2:移动端v-html解析后有这么个样式;由于layui-table太宽导致页面放不下,现在需要把他上一级的div标题添加滚动条,但是div又没有类名:

解决方案:

css 复制代码
/* 选中"包含 .layui-table 的 div"并给它加滚动 */
div:has(> .layui-table) {
  overflow: auto;
  /* 想横向滚动就再加 */
  max-width: 100%;
}

:has() 让 CSS 第一次拥有了"父级/前兄弟"判断能力。小编用上面两个需求实战开篇,是不是让大家感觉到眼前一亮的感觉!后面小编还会再给出 10 个"哇,原来还能这样"的实用场景,全部复制即可用,一次性把 :has() 写进肌肉记忆。

第二章 :has() 语法速记

  • A:has(B) :A 内部任意位置包含 B(后代)
  • A:has(> B) : A 的直接子节点是 B
  • A:has(+ B) :A 的下一个相邻兄弟是 B
  • A:has(~ B) :A 后面所有兄弟里至少有一个 B
  • A:has(B, > C) : 多条件:或关系
css 复制代码
A:has(B)          /* A 内部任意位置包含 B(后代) */
A:has(> B)         /* A 的直接子节点是 B */
A:has(+ B)         /* A 的下一个相邻兄弟是 B */
A:has(~ B)         /* A 后面所有兄弟里至少有一个 B */
A:has(B, > C)      /* 多条件:或关系 */

注意:

  • "前兄弟"选不到,只能"向后"选;需要反向时让共同祖先兜底 / 或者用正常的+ > ~ 都可
  • 参数里可以放任意"复合选择器",但不能放伪元素(::before 等)。

第三章 实用场景

|----|--------------------------|-----------------------------------------------------------------------|-----------|
| | 场景 | 代码 | 体验提升点 |
| 1 | 空购物车隐藏结算栏 | .cart:has(.item:empty) .checkout { display:none; } | 无需 JS 计算 |
| 2 | 表单项必填且未填时高亮提交按钮 | form:has(.required:invalid) buttontype=submit { opacity:.4; } | 实时反馈 |
| 3 | 图片没加载完先不显示容器 | .img-box:has(imgloading) { visibility:hidden; } | 防止抖动 |
| 4 | 标签页当前高亮 | .tabbar:has(#tab2:checked) labelfor=tab2 { color:var(--active); | 纯 CSS 标签页 |
| 5 | 评论区有敏感词直接隐藏 | .comment:has(.word-filter:checked) { display:none; } | 运营秒审 |
| 6 | 表格某列全为 0 时整列隐藏 | table:has(td:nth-child(3):not(:zero)) .col-3 { visibility:collapse; } | 数据自适应 |
| 7 | 文章里出现代码块就加顶部"复制"按钮 | .post:has(pre) .copy-btn { display:inline-flex; } | 渐进增强 |
| 8 | 卡片组图任意一张加载失败就换兜底图 | .gallery:has(img:error) { background-image:url(fallback.svg); } | 故障容灾 |
| 9 | 导航栏吸顶时给主体加 padding(避免跳动) | body:has(.nav.stuck) main { padding-top:60px; } | 滚动顺滑 |
| 10 | 深色模式自动判断(检测到深色图片) | body:has(imgsrc\*="dark") { color-scheme:dark; } | 彩蛋式主题 |

第四章 性能 & 兼容性

  • 尽量用"近距离"关系

:has(> B) / :has(+ B) 搜索深度最浅,回流最小。

  • 不要嵌套 :has()

浏览器已做短路优化,但两层以上可读性骤降,可拆中间 class。

  • 老浏览器优雅降级模板
css 复制代码
/* 1. 现代浏览器 */
@supports selector(:has(+ *)) {
  .v-modal:has(+ .login-dialog) { ... }
}
/* 2. 旧浏览器走 JS 方案 */
.v-modal.login-active { ... }   // 由脚本添加
  • 与 Tailwind / UnoCSS 结合
javascript 复制代码
<div class="md:has-[table]:overflow-x-auto">

第五章 总结

:has() 把 CSS 从"只能向后"升级为"可以向前看",让"结构判断"真正留在了样式层。 今天遮罩改色,明天空态隐藏,后台表格折叠......凡是以前必须写 JS 的"如果里面有××就改外面",现在都可以先试试 :has() ------ 一行代码,体验翻倍。

相关推荐
weixin_382395233 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__4 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.4 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~5 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华6 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子8 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅8 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni9 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学10 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端