CSS无比强大的:has伪类的使用技巧
:has
伪类是一个非常强大的伪类,强大到难以置信,可以做很多梦寐以求的事情,很多以前只能更改 dom 结构 或者只能用 JS 才能实现的功能现在也能纯 CSS 实现了,一起看看吧。
一、简单介绍一下:has
:has
伪类的语法非常简单,表示满足一定条件后,就会匹配该元素。
例如,下面的选择器只会匹配直接包含img
子元素的a
元素:
a:has(> img)
再例如,下面的选择器只会匹配其后紧跟着p
元素的h1
元素:
h1:has(+ p)
以我个人的理解来看,去除:has()
后,剩下的选择器仍然是完整的:
a>img
加上:has()
后,可以选中最前面的元素a
。
好了,语法其实就这么多,估计没啥吸引力,关键是实际应用。下面通过几个实例来感受一下:has
伪类的强大魅力~
温馨提醒:兼容性要求需要 Chrome 101+,并且开始实验特性(105+正式支持),Safari 15.4+,Firefox 官方说开启实验特性可以支持,但是实测并没有(???)
二、表单元素必填项
先来看一个简单例子,下面有一个表单元素,有一些是必填项。
<form> <item> <label>用户名</label> <input required> </item> <item> <label>备注</label> <input> </item> </form>
效果:
现在可以通过:has
在必填项的前面加上红色的星号:
label:has(+input:required)::before{ content: '*'; color: red; }
这个应该还比较好理解,通过:has
和+
可以选中满足条件的label
,然后再生成::before
伪元素。如果是在以前,可能需要手动添加类名,或者改变html
的书写顺序。
三、拖拽指定区域
有些时候列表需要有拖拽功能,但为了拖拽体验,不影响列表内部操作,可能需要指定小部分区域可拖拽,例如:
HTML结构如下:
<div class="content"> <div class="item">列表<span class="thumb"></span></div> <div class="item">列表<span class="thumb"></span></div> <div class="item">列表<span class="thumb"></span></div> </div>
现在我们希望在hover
时出现拖拽手柄,按住拖拽手柄才可以拖拽,看着好像非常麻烦,但是现在借助:has
伪类可以轻易实现,关键CSS如下:
.thumb{ /**/ opacity: 0 } .item:hover .thumb{ opacity: 1; } .item:has(.thumb:hover){ -webkit-user-drag: element; }
这里的:has
表示当.thumb
处于:hover
状态时选中该元素,从而给.item
添加可拖拽属性,效果如下:
完整Demo,点击这里
四、多层级 hover
有一个多层级的结构,例如:
<div class="box-1"> <div class="box-2"> <div class="box-3"></div> </div> </div>
如果给div
添加hover
样式:
div:hover{ outline:4px dashed rebeccapurple }
效果如下:
可以看到,当hover
到里层元素时,外层元素也触发了hover
样式。这有点像JS
中的冒泡效果,那如何让hover
的时候只触发当前的元素呢?也就是排除掉他的父级元素,没错,:has
可以很好的解决这个问题。
div:not(:has(:hover)):hover{ outline:4px dashed rebeccapurple }
是不是越来越绕了?别急,我们拆解分析一下,div:has(:hover)
表示有子元素正处于hover
的div
,比如当hover
到box-3
时,div:has(:hover)
选中的就是除box-3
以外的两个父级,然后加上:not
就刚好反过来,只选中box-3
本身,可以理解吗?这个可以下来多试试,实际效果如下:
完整 demo:点击这里
在一些可视化拖拽平台,各种嵌套的组件中会很有作用。
五、评星组件
这个功能也是非常适合用:has
来实现的,HTML 结构如下:
<star> <input name="star" type="radio"> <input name="star" type="radio"> <input name="star" type="radio"> <input name="star" type="radio"> <input name="star" type="radio"> </star>
简单修饰一下:
star{ display: flex; } star [type="radio"]{ appearance: none; width: 40px; height: 40px; margin: 0; cursor: pointer; background: #ccc; transition: .3s; -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 512 512'%3E %3Cpath d='M462.3 62.6C407.5 15.9 326 24.3 275.7 76.2L256 96.5l-19.7-20.3C186.1 24.3 104.5 15.9 49.7 62.6c-62.8 53.6-66.1 149.8-9.9 207.9l193.5 199.8c12.5 12.9 32.8 12.9 45.3 0l193.5-199.8c56.3-58.1 53-154.3-9.8-207.9z'%3E%3C/path%3E %3C/svg%3E") center / 80% no-repeat; }
效果如下:
下面要做交互功能,当:hover
或者:checked
时,当前元素和当前元素之前的元素都触发选中。
在之前,由于只有后置兄弟选择器~
,所以必须要将 dom 元素更改顺序,然后通过其他方式在视觉上再翻转过来。现在有了:has
,这些奇技淫巧都可以说拜拜了,实现如下:
star [type="radio"]:hover, star [type="radio"]:has(~:hover), star:not(:hover) [type="radio"]:checked, star:not(:hover) [type="radio"]:has(~:checked){ background: orangered; }
相信不算太复杂,[type="radio"]:has(~:hover)
表示选中当前hover
元素之前的元素,所以可以轻易的实现评分的效果:
完整 demo:点击这里
六、日期范围选择
如果说上面这些例子有其他代替方案,或者说用一点点 JS 也能实现,那下面来一个重磅级的案例。在以前,就算靠 JS 也会有一些麻烦,但是有了:has
,一切都变得简单了。
假设 HTML 结构如下:
<div class="date"> <span>1</span> <span>2</span> <span>3</span> ... <span>30</span> <span>31</span> </div>
这部分交互有两个部分,一个是鼠标滑过,还有一个是选中。
我们先看选中的功能,当有两个元素被选中时,这两个元素之间的元素都会匹配上,假设选中的类名是select
:
<div class="date"> <span>1</span> <span>2</span> <span class="select">3</span> ... <span class="select">30</span> <span>31</span> </div>
那么,如何让这一片区域的元素都匹配上呢?答案就是通过:has
找到select
之前的元素,再结合~
匹配之后的元素,两者结合就可以匹配到中间的元素了,具体实现如下:
.select, .select~span:has(~.select){ background-color: blueviolet; color: #fff; }
效果如下:
然后是 hover
的效果,假设有一个已经被选中了:
<div class="date"> <span>1</span> <span>2</span> <span class="select">3</span> ... <span>30</span> <span>31</span> </div>
现在需要再鼠标滑过的时候,将鼠标的终点和已选中的范围都匹配上,这个就稍微有些复杂了,我们需要考虑鼠标在已选中元素之前还是之后,分别用:has
进行判断,实现如下:
span:hover~span:has(~.select), .select~span:has(~:hover) { background-color: blueviolet; color: #fff; }
是不是有些晕了?第一条表示鼠标在已选中之前,匹配当前hover
之后、.selelct
之前的元素, 第二条表示已选中之后,匹配.selelct
之后、hover
之前的元素,实际效果如下:
还有一个问题,需要区分一下选中两个和只选中一个的情况,因为两个表示区间选择已经完成,此时hover
不会有效果,借助:has
伪类,可以很轻易的区分子元素的个数,如下:
.date:not(:has(.select~.select)){ /*匹配到没有两个.select的父级*/ }
.select~.select
表示选中.select
后面的.select
,也就是表示至少有两个.select
,然后通过:has
就可以区分这两种情况了。
.date:not(:has(.select~.select)) .select, .date:not(:has(.select~.select)) span:hover{ background-color: transparent; color: inherit; outline: 2px solid blueviolet; outline-offset: -2px; } .date:not(:has(.select~.select)) span:hover~span:has(~.select), .date:not(:has(.select~.select)) .select~span:has(~:hover) { background-color: blueviolet; color: #fff; }
元素的选中时通过JS
实现的,这时候的JS
完全就只是工具人了,和视觉完全不相干,只需要记录选中的元素,逻辑极其简单,如下:
date.addEventListener('click', ev => { const current = date.querySelectorAll('.select'); if (current.length == 2) { current.forEach(el => { el.classList.remove('select') }) } ev.target.classList.add('select') })
然后就可以得到这样的效果了。
完整 demo:点击这里
1. 本站所有文章教程及资源素材均来源于网络与用户分享或为本站原创,仅限用于学习和研究。
2. 如果内容损害你的权益请联系客服QQ:1642748312给予处理。
码云笔记 » CSS无比强大的:has伪类的使用技巧