CSS 有了:has 伪类可以做些什么?
相信大家最近对 :has
都有所耳闻,规范提及了那么久,却迟迟没有动静,最近浏览器终于开始支持了~:tada::tada:
:has
伪类是一个非常强大的伪类,强大到难以置信,可以做很多梦寐以求的事情,很多以前只能更改 dom 结构 或者只能用 JS 才能实现的功能现在也能纯 CSS 实现了,一起看看吧
一、简单介绍一下 :has
:has
伪类的语法非常简单,表示满足一定条件后,就会匹配该元素。
例如,下面的选择器只会匹配直接包含 img
子元素的 a
元素:
再例如,下面的选择器只会匹配其后紧跟着 p
元素的 h1
元素
从形式上来看,去除 :has()
后,剩下的选择器仍然是完整的
加上 :has()
后,可以选中最前面的元素 a
。
好了,语法其实就这么多,估计没啥吸引力,关键是实际应用。下面通过几个实例来感受一下 :has
伪类的强大魅力~
温馨提醒:兼容性要求需要 Chrome 101+,并且开始实验特性(105+正式支持),Safari 15.4+,Firefox 官方说开启实验特性可以支持,但是实测并没有(???)
二、表单元素必填项
先来看一个简单例子,下面有一个表单元素,有一些是必填项
现在可以通过 :has
在必填项的前面加上红色的星号
这个应该还比较好理解,通过 :has
和 +
可以选中满足条件的 label
,然后再生成 ::before
伪元素。如果是在以前,可能需要手动添加类名,或者改变 html
的书写顺序
你也可以访问线上完整 demo: has+required(codepen.io) [1] 或者 has+required(runjs.work) [2]
三、拖拽指定区域
有些时候列表需要有拖拽功能,但为了拖拽体验,不影响列表内部操作,可能需要指定小部分区域可拖拽,例如
HTML 结构如下
现在我们希望在 hover
时出现拖拽手柄,按住拖拽手柄才可以拖拽,看着好像非常麻烦,但是现在借助 :has
伪类可以轻易实现,关键 CSS 如下
这里的 :has
表示当 .thumb
处于 :hover
状态时选中该元素,从而给 .item
添加可拖拽属性,效果如下
你也可以访问线上完整 demo: drag_thumb(codepen.io) [3] 或者 drag_thumb(runjs.work) [4]
四、多层级 hover
再来看一个例子,早在四年前就遇见过的 CSS 多层级 hover 问题 [5] ,现在终于有解了~
是这样的,有一个多层级的结构,例如
如果给 div
添加 hover
样式
效果是这样
可以看到,当 hover
到里层元素时,外层元素也触发了 hover
样式。这有点像 JS
中的冒泡效果,那如何让 hover
的时候只触发当前的元素呢?也就是排除掉他的父级元素,没错, :has
可以很好的解决这个问题
是不是越来越绕了?别急,我们拆解分析一下, div:has(:hover)
表示有子元素正处于 hover
的 div
,比如当 hover
到 box-3
时, div:has(:hover)
选中的就是除 box-3
以外的两个父级,然后加上 :not
就刚好反过来,只选中 box-3
本身,可以理解吗?这个可以下来多试试,实际效果如下
你也可以访问线上完整 demo: CSS hover(codepen.io) [6] 或者 CSS hover(runjs.work) [7]
在一些可视化拖拽平台,各种嵌套的组件中会很有作用
五、评星组件
这个功能也是非常适合用 :has
来实现的,HTML 结构如下
简单修饰一下
效果如下
下面要做交互功能,当 :hover
或者 :checked
时,当前元素和当前元素之前的元素都触发选中。
在之前,由于只有后置兄弟选择器 ~
,所以必须要将 dom 元素更改顺序,然后通过其他方式在视觉上再翻转过来。现在有了 :has
,这些奇技淫巧都可以说拜拜了,实现如下
相信不算太复杂, [type="radio"]:has(~:hover)
表示选中当前 hover
元素之前的元素,所以可以轻易的实现评分的效果
你也可以访问线上完整 demo: CSS star (codepen.io) [8] 或者 CSS rate(runjs.work) [9]
六、日期范围选择
如果说上面这些例子有其他代替方案,或者说用一点点 JS 也能实现,那下面来一个重磅级的案例。在以前,就算靠 JS 也会有一些麻烦,但是有了 :has
,一切都变得简单了~
假设 HTML 结构如下
这部分交互有两个部分,一个是鼠标滑过,还有一个是选中。
我们先看选中的功能,当有两个元素被选中时,这两个元素之间的元素都会匹配上,假设选中的类名是 select
那么,如何让这一片区域的元素都匹配上呢?答案就是通过 :has
找到 select
之前的元素,再结合 ~
匹配之后的元素,两者结合就可以匹配到中间的元素了,具体实现如下
效果如下
然后是 hover
的效果,假设有一个已经被选中了
现在需要再鼠标滑过的时候,将鼠标的终点和已选中的范围都匹配上,这个就稍微有些复杂了,我们需要考虑鼠标在已选中元素之前还是之后,分别用 :has
进行判断,实现如下
是不是有些晕了?第一条表示鼠标在已选中之前,匹配当前 hover
之后、 .selelct
之前的元素, 第二条表示已选中之后,匹配 .selelct
之后、 hover
之前的元素,实际效果如下
还有一个问题,需要区分一下选中两个和只选中一个的情况,因为两个表示区间选择已经完成,此时 hover
不会有效果,借助 :has
伪类,可以很轻易的区分子元素的个数,如下
.select~.select
表示选中 .select
后面的 .select
,也就是表示至少有两个 .select
,然后通过 :has
就可以区分这两种情况了
元素的选中时通过 JS
实现的,这时候的 JS
完全就只是工具人了,和视觉完全不相干,只需要记录选中的元素,逻辑极其简单,如下
然后就可以得到这样的效果了
你也可以访问线上完整 demo: CSS date-range(codepen.io) [10] 或者 CSS date-range(runjs.work) [11]
等待时机成熟,xy-ui 中的 date-picker [12] 也会跟进使用这一方案~
七、别等了,现在就学起来
以上就是 CSS 在支持 :has
伪类后,可以想到的一些应用场景,是不是非常强大?这么好用的伪类,还不学一下吗?
当然其作用远不止这些,可以这么讲,以前需要考虑 dom 顺序的场景都可以解决了,以后的 dom 将更加语义化,大部分交互状态伪类都可以很好地结合在一起,80% 以上交互相关的 JS 代码都可以去掉了,JS 可以更加安心的去做自己该做的事情了,比如数据转换,业务逻辑等等。
目前一些可以手动指定 Chrome 版本的 Electron 应用也可以马上用起来了,其他的再等一年,公司的一些内部项目就可以大胆用起来了。最后,如果觉得还不错,对你有帮助的话,欢迎点赞、收藏、转发❤❤❤
References
[1]
has+required(codepen.io): https://codepen.io/xboxyan/pen/yLjyopY
[2]
has+required(runjs.work): https://runjs.work/projects/5819b7bc43474966
[3]
drag_thumb(codepen.io): https://codepen.io/xboxyan/details/QWrwMaL
[4]
drag_thumb(runjs.work): https://runjs.work/projects/502506f18ee34b6e
[5]
CSS 多层级 hover 问题: https://segmentfault.com/q/1010000016225343/a-1020000042380822
[6]
CSS hover(codepen.io): https://codepen.io/xboxyan/pen/eYrmEyd
[7]
CSS hover(runjs.work): https://runjs.work/projects/2a7e819e473c47af
[8]
CSS star (codepen.io): https://codepen.io/xboxyan/pen/abGzyEw
[9]
CSS rate(runjs.work): https://runjs.work/projects/b1ad1cf84b314dfd
[10]
CSS date-range(codepen.io): https://codepen.io/xboxyan/details/dyePzVd
[11]
CSS date-range(runjs.work): https://runjs.work/projects/85ba26f3ff18414b
[12]
date-picker: https://xy-ui.codelabo.cn/docs/#/xy-date-picker
评论