好物分享 | 🤑这次,has伪类好像真的来了
未必素娥无怅恨,玉蟾清冷桂花孤。——晏殊·中秋月
前后忙碌月多,整个项目组单独成立分公司,开不完的大会,吃不完的大饼🤮...
终于在中秋节后,一切又归于平静,在下又能网上冲浪,四处摸鱼了👋🐟。
今儿个不写动效(其实是灵感枯竭),今儿个来聊聊一位重磅的、闻名已久的、肥肠好使的css伪类家族成员::has伪类。
我们好像在哪见过?
在下相信,跟在下一样到处👋🐟的同学,一定或多或少也听说过这个has伪类,但是好像又从来都没有用过,似曾相识,却素未谋面,朦胧而又神秘。(👂:我们好像在哪儿见过,你记得吗?)
咳咳...
其实说到has伪类,已经不能算是一个新的东西了,has伪类的规范其实制定的很早很早。那到底有多早呢?这么说吧,如果说规范制定算作has伪类的诞生的话,那与在下年纪相仿。
但是为什么一直没有被用起来呢?究其原因,其实是因为浏览器厂商们的担忧——性能消耗。
众所周知,页面DOM的渲染是构建DOM树从上往下渲染的,而这位has伪类,却是通过子元素去匹配对应的父元素,设置父元素的样式,那么势必它就一定要等子元素渲染出来以后才能去进行后续操作,这样一种方式必然会影响网页的渲染速度,从而影响用户体验。
偏偏好巧不巧,has伪类的功能却又非常强大,一定会被大量使用,从而就会导致无法预估的性能消耗,所以在设计之初,has伪类就被明确规定不能用于CSS样式中了。
我曾需要你但你不在
不知道是否只有在下遇到类似这样的问题:子元素样式发生变化,父元素也需要随之而发生变化。
而当时,在下第一反应就是,CSS中有子选择器
,那有没有父选择器
。查阅各种资料后无果,最终只能通过JS
操作dom完成。
(从小就被教育尽量不要直接操作dom元素的在下内心充满了罪孽感...😭)
而这个与在下年纪相仿的has伪类,其实就是在下心心念念的父选择器
,满足条件,却不能被使用。
我需要你,但你不在,又是夺摩的悲哀。
2022,我们终将相遇
盼望着,盼望着,它终于来了。
这里有一家公司,它叫Igalia,具体是谁,到底干啥的,咱不需要了解。
咱只要知道,它搞定了一个事情:解决了浏览器几十年来无法解决的性能问题。
咋解决的在下不了解,在下只知道,这意味着曾经在下可望不可即的has伪类,它终于要来了!!
就在2022!就在这俩月!九栽锦天!
截止目前(2022/9/14),Chrome105及之后版本、Edge最新版本以及Safari已经完成了对 :has伪类 的支持,而Firefox也开启了实验支持。(快看快看!!)
所以,就照着今年这个浏览器版本迭代的速度,很快在下就可以在项目中愉快的使用父选择器
了。
初次见面,请多关照
闲话扯了一堆,今儿个在下就简单介绍一下这位又老又新的朋友——:has()
其实 :has() 的语法比较简单,套用MDN的介绍:
:has
代表一个元素,其给定的选择器参数(相对于该元素的:scope
)至少匹配一个元素。注意,请记住介绍里括号中的内容(
:scope
)
而 :has() 的使用语法也很简单:
:has( <相对选择器列表> )
他可以接收一个元素,也可以接收一个元素列表,通过接收的参数至少匹配一个元素(当然前提是你得写对)。
接下来就跟随在下去一个个浅尝和测试一下这位新成员的用法。
环境:Chrome 105.0.5195.102(正式版本)(64 位)
.class
class选择器,也是最常用的选择器之一,那我们就从最常用的开刀。
<div class="demo">
我有一个class为foo的子元素
<div class="foo"></div>
</div>
<div class="demo">
我有一个class为bar的子元素
<div class="bar"></div>
</div>
匹配包含class为foo的元素(即匹配.foo的父元素)。
.demo:has(.foo) {
background-color: aqua;
}
通过一个简单的尝试,在下成功的实现了对.foo
的父元素的选择,但是对.bar
的父元素没有进行任何操作,从而实现了父选择器
的功能。
咱们接着尝试。
#id
<div class="demo0">
我有一个id为foo的子元素
<div id="foo"></div>
</div>
<div class="demo0">
我有一个id为bar的子元素
<div id="bar"></div>
</div>
匹配包含id为foo的元素(即匹配#foo的父元素)。
.demo0:has(#foo) {
background-color: yellowgreen;
}
element
匹配包含span元素的元素(即匹配span的父元素)
<div class="demo1">
我有span元素
<span></span>
</div>
<div class='demo1'>
我没有span元素
</div>
.demo1:has(span) {
background-color: skyblue;
}
传递一个列表
前面MDN的介绍中提到, :has() 可以接收一个列表,所以在下也试试。
匹配包含class为foo或者包含span元素的父元素(即.foo或者span的父元素)
<div class="demo">
我有一个class为foo的子元素
<div class="foo"></div>
</div>
<div class="demo">
我有一个id为bar的子元素
<div id="bar"></div>
</div>
<div class="demo">
我有一个span子元素
<span></span>
</div>
.demo:has(.foo, span) {
background-color: blanchedalmond;
}
:has()嵌套
上一个是匹配包含.foo或者span的父元素,那如果想要匹配同时包含.foo和span的父元素怎么操作?
当然是嵌套啦
<div class="demo">
我有一个class为foo的子元素
<div class="foo"></div>
</div>
<div class="demo">
我有一个span子元素
<span></span>
</div>
<div class="demo">
我有一个span子元素和一个class为foo的子元素
<span></span>
<div class="foo"></div>
</div>
.demo:has(span):has(.foo) {
background-color: gold;
}
> element
匹配包含直接class为foo子元素的元素(即.class的直接父元素,非其他爷爷元素)
<div class="demo2">
我有直接子元素i
<i></i>
</div>
<div class="demo2">
我有非直接子元素i
<span>
<i></i>
</span>
</div>
.demo2:has(> i) {
background-color: orange;
}
注意,匹配直系父元素时,has()里不能用 :has(.demo2 > i) 的形式
原因是前面MDN介绍中提到的
:scope
,如果用上面这个形式,会找不到.demo2,所以如果要匹配直系父元素,请使用 :has(> 子元素) 的方式,
现在部分编辑器可能会有报错提示,但是实际上是可以匹配成功的
+ element
相信同学们都用过element + element
的选择器,称为兄弟选择器,它是用来匹配紧跟在元素后面的第一个元素。
那么能匹配紧跟在元素前面的第一个元素吗?
:has():当然可以!
<div>
<div class="demo3">
我有紧跟的兄弟元素i
</div>
<i>
</i>
</div>
<div>
<div class="demo3">
我有不紧跟的兄弟元素i
</div>
<span></span>
<i></i>
</div>
.demo3:has(+ i) {
background-color: pink;
}
~ element
同理,element ~ element
用来匹配元素后面的所有对应兄弟元素,:has() 也可以匹配对应兄弟元素前面的元素
<div>
<div class="demo4">
我后面有兄弟span
</div>
<i></i>
<span></span>
</div>
<div>
<div class="demo4">
我后面没有兄弟span
</div>
<i></i>
<i></i>
</div>
.demo4:has(~ span) {
background-color: green;
}
编辑器可能会报错,但是不影响
[attribute]
咱可以通过element[attribute]
来匹配一个包含某个属性的元素,反之也可以用 :has() 来匹配一个包含某个拥有attribute属性子元素的父元素。(有点绕...)
匹配包含有name属性的span元素的父元素
<div class="demo7">
我有子元素span,它有name属性
<span name="test"></span>
</div>
<div class="demo7">
我有子元素span,它没有name属性
<span></span>
</div>
.demo7:has(span[name]) {
background-color:aqua;
}
其他与匹配属性相关的方法就不列举了。
:hover等其他伪类
同时 :has() 也可以与其他伪类匹配使用,这里在下列举的是 :hover 伪类
匹配子元素被鼠标悬停的得父元素
<div class="demo">
我是我是按钮1的父元素
<div class="btn">我是按钮1</div>
</div>
<div class="demo">
我是我是按钮2的父元素
<div class="btn">我是按钮2</div>
</div>
.demo:has(.btn:hover) {
background-color: aquamarine;
}
只是一个简单的分享,为了避免篇幅太长,其他伪类在下也不再赘述了。
往后余生,来日方长
通过跟随在下进行了少量的尝试以后,是否已经对 :has() 有了初步的了解,亦或是已经熟练掌握?
其实 :has() 伪类的用法简单,应用场景却非常之广泛,而其他更具体的灵活使用方法,则需要在漫长的业务代码中各自探索了。
很多同学肯定在想:这不就是个简单的CSS伪类吗,也谈不上功能强大把?
试想一下,当我们 以body
或者html
为最终的父元素,从页面的任何一个元素逐层网上查询,当我们拿到这个最终父元素后,是不是顺其自然的就可以拿到任何一个子元素了?
那么,是不是也就意味着,在处理某一个元素被修改的同时,在八百里开外的某个元素也需要随之修改样式时,我们将不在依赖JS,或者变量传来传去,而是可以直接通过 :has() 伪类进行查找匹配后直接修改😉。
好吧,听起来好像也没有很强大,算了,遇到在聊吧。
转载自:https://juejin.cn/post/7143164652032098317