前言
在 CSS 选择器大家族中新增了几个特殊的选择器:
:is()
:where()
:not()
:has()
我们称之为**逻辑选择器,**现在很多组件库已经开始使用这类选择器,但是当你看到这些选择器时,是不是一头雾水,这到底是啥意思,选择的又是哪些元素,今天我们就来深入剖析下这几个有意思的选择器。
:is()
选择器
基础用法
**:is()**
CSS 伪类函数以选择器列表作为参数,并选择该列表中任意一个选择器可以选择的元素。这对于以更紧凑的形式编写大型选择器非常有用。
在之前,对于多个不同父容器的同个子元素的一些共性样式设置,可能会出现如下 CSS 代码:
css
.header div:hover,
.main div:hover,
.footer div:hover {
color: green;
cursor: pointer;
}
而如今,有了 :is()
选择器,则上述代码可以被改写为:
css
:is(.header, .main, .footer) div:hover{
color: green;
cursor: pointer;
}
可以看到,:is()
选择器不像是新语法,更像是一个语法糖,用于简化复杂 CSS 的写法:
在网上找了一张动图,一目了然。
支持多层叠用
考虑下面的代码
html
<div>
<span>div > span</span>
</div>
<div>
<i>div > i</i>
</div>
<p>
<span>p > span</span>
</p>
<p>
<i>p > i</i>
</p>
<p>
<div>p > div</div>
</p>
<div>
<p>div > p</p>
</div>
我们希望 div 和 p 标签下的 span 和 i 标签的内容颜色为绿色,CSS 怎么写:
css
div span,
div i,
p span,
p i {
color: green;
}
现在有了 :is()
选择器,代码可以简化为:
css
:is(div, p) :is(span, i) {
color: green;
}
对,:is()
选择器可以多次层叠使用,类似于我们高中学的排列组合。
那如果我们希望这些元素是在 hover 或是 focus 状态下才改变字体颜色呢,普通 CSS 怎么写:
css
div span:hover,
div i:hover,
p span:hover,
p i:hover,
div span:focus,
div i:focus,
p span:focus,
p i:focus{
color: green;
}
嗯,已经开始有点烦了,那让 :is()
选择器来帮我们解决烦恼:
css
:is(div, p) :is(span, i):is(:hover, :focus) {
color: green;
}
好了,瞬间精神了。
:is()
选择器的优先级
:is()
选择器前面说了只是一个简化的语法糖,所以它的优先级是遵循组合出的 CSS 的选择器的优先级,即ID -> 类 -> 元素
的优先级。
考虑如下代码:
html
<div>
<p class="demo-class">元素选择器</p>
</div>
<div>
<p class="demo-class">类选择器</p>
</div>
<div>
<p class="demo-class" id="demoId">ID 选择器</p>
</div>
css
div :is(p) {
color: blue;
}
div :is(.demo-class) {
color: green;
}
很明显,因为类选择器 .demo-class
的优先级高于元素选择器 p
,所以 p
标签的文本颜色为 green
绿色。一起看下结果:
好,一切都在我们的掌控中,考虑下如下 CSS 代码:
css
div :is(.demo-class) {
color: green;
}
div :is(p, #demoId) {
color: red;
}
将上边的 CSS 拆开来看:
css
div .demo-class { color: green }
div p { color: red }
div #demoId { color: red }
根据上边拆分的结果,分析下:
因为 ID 选择器的优先级最高,所以第三个 p 标签文本颜色为红色,而其他两个 p 标签因为都有 .demo-class
类,且类选择器优先级高于元素选择器,所以第一,二个 p 标签内容为绿色
但是事实却是:三个 p 标签的文本颜色都是红色,如下所示:
这是由于,:is()
的优先级是由它的选择器列表中优先级最高的选择器决定的。我们不能把它们割裂开来看。
对于 div :is(p, #demoId)
内部有一个 id 选择器,因此,被该条规则匹配中的元素,全部都会应用 div #demoId
这一级别的选择器优先级。
这里非常重要,再强调一下,对于 :is()
选择器的优先级,我们不能把它们割裂开来看,它们是一个整体,优先级取决于选择器列表中优先级最高的选择器。
:is()
不能选择伪元素
:is()
不能选择伪元素,所以下面的代码是行不通的:
css
div:is(::after, ::before) {
display: block;
}
伪元素列表:链接
兼容性
:where()
选择器
基础用法
了解了 :is()
选择器,接下来我们来看下 :where()
选择器。
它们两个之间具有很强的关联性,甚至可以说是双胞胎。还是最开始的例子,改成 :where()
选择器
css
:where(.header, .main, .footer) div:hover{
color: green;
cursor: pointer;
}
上面代码功能类似于如下(意我这里使用的是类似,所以它们是不一样的):
css
.header div:hover,
.main div:hover,
.footer div:hover {
color: green;
cursor: pointer;
}
你可能会说了,这不跟 :is()
选择器一样吗?
那他们之间没有区别吗?
:is()
和 :where()
的区别
先说结论::is()
和 :where()
的区别在于::where()
的优先级总是 0 。
考虑下面的代码:
html
<div>
<p class="demo-class">where & is</p>
</div>
css
div :is(p) {
color: red;
}
div :where(.demo-class) {
color: blue;
}
结果:
可以看到,最后文字的展现效果是红色。
分析下:
- 首先我们使用
:is()
选择器定义了 p 元素的文本颜色为红色 - 之后我们又使用
:where()
选择器使用类选择器定义了该 p 标签内的文本颜色为蓝色 - 结果是红色,很显然,
:where()
选择器没有生效。
虽然 :where()
选择器使用的优先级更高的类选择器,但是依然被低优先级的元素选择器样式覆盖了,这很好的论证了 :where()
选择器的优先级为 0 的观点。
除此之外,:is()
和 :where()
无任何差别。
而且,:is()
和 :where()
可以嵌套使用,考虑如下代码:
css
:is(div, p) :where(span, i) {
color: red;
}
此代码同时使用了:is()
和 :where()
实现了嵌套功能,但是因为存在 :where()
选择器,所以他们的优先级都为 0;
兼容性
:not()
选择器
:not()
用来匹配不符合一组选择器的元素。由于它的作用是防止特定的元素被选中,它也被称为_反选伪类_(negation pseudo-class)
基础用法
考虑如下代码:
html
<div>
<p class="demo-class1">where & is</p>
<p class="demo-class2">where & is</p>
<p class="demo-class3">where & is</p>
<p class="demo-class4">where & is</p>
<p class="demo-class5">where & is</p>
<p class="demo-class6">where & is</p>
</div>
css
div p:not(.demo-class1) {
color: red;
}
根据 :not()
选择器器的定义你应该能够理解以上代码的意思:div 下的 p 标签文本的颜色设置为红色,除了那个 class 为 demo-class1
的 p 元素,看下结果:
符合预期。
而且,:not()
选择器还支持选择器列表为参数进行多选,考虑如下代码:
css
div p:not(.demo-class1, .demo-class2) {
color: red;
}
这样,class 名为 demo-class1
和 demo-class2
的元素都不会被选中。
注意点
使用 :not()
时,有几种不寻常的效果和结果需要注意:
- 可以使用此伪类编写无用的选择器。例如,
:not(*)
匹配任何不是元素的元素,这显然是荒谬的,所以这个附加的规则将永远不被应用。 - 可以利用这个伪类提高规则的优先级。例如,
#foo:not(#bar)
和#foo
都将匹配相同的元素,但是具有两个 id 的选择器具有更高的优先级。 :not()
伪类的优先级将由其逗号分割的参数中优先级最高的选择器指定;提供与:not(:is(argument))
相同的优先级。:not(.foo)
将匹配任何非.foo
的元素,包括 html 和 body。- 这个选择器将匹配任意"不是一个 X"的元素。当与后代选择器一起使用,这可能令人惊讶,因为有多种路径可以选择一个目标元素。例如,
body :not(table) a
仍将应用 table 中的链接,因为 tr、tbody、th、td(en-US)、caption 等都可以匹配选择器 :not(table) 部分。 - 你可以同时否定多个选择器。例如:
:not(.foo, .bar)
等同于:not(.foo):not(.bar)
。 - 如果传递给
:not()
伪类的选择器无效或者浏览器不支持,则整个规则都将是无效的。克服这种行为的有效方式是使用::is 伪类,它接受一个可容错选择器列表。例如:not(.foo, :invalid-pseudo-class)
将使整个规则无效,但是:not(:is(.foo, :invalid-pseudo-class))
将匹配任何(包括 html 和 body)不是.foo
的元素。
兼容性
注意:列表式的选择器和单一的选择器的支持度不一样。
:has()
选择器
基础用法
:has()
选择器非常有意思,它的出现填补了 CSS 选择器不能选择父元素或是先前兄弟元素的空白。我们一起来看下它是怎么使用的,考虑下面的代码:
html
<p><span class="demo">1</span></p>
<p>2</p>
<p><i>3</i></p>
<p><span>4</span></p>
<p>
<span>
<span class="demo">5</span>
</span>
</p>
css
p:has(.demo, i) {
color: red;
}
分析下:
- html 的结构为五个 p 标签,内部嵌套各不一样,注意最后一个 p 标签内部嵌套了两层
- css 代码表示,选择当前全部的 p 标签,
:has(.demo, i)
表示:这全部的 p 标签中我需要最下筛选,其中子元素含有demo
类的和含有 i 标签的筛选出来作为最后的值返回并设置文本颜色为红色。
那么被筛选出来的 p 标签应该是哪个呢?
- 第一个 p 标签有一个类为
demo
的 span 子元素,命中 - 第二个 p 标签没有子元素,不符合
- 第三个 p 标签有一个子元素为 i 标签,命中
- 第四个 p 标签有一个
span
标签子元素,但是没有demo
类,不符合 - 第五个 p 标签有一个嵌套的 span 标签,span 之下又嵌套了一个 span 标签,且这个 span 标签还有
demo
类,那这个符合条件吗?先打个问号?
一起看下最后结果:
其中,1 2 5 的 p 标签内容被渲染为红色,意味着这几个命中了规则,很显然,第五个标签也符合条件。
所以总结下 :has()
的选择逻辑:
- 首先必须选择中一些可进行筛选的父级标签
- 之后
:has()
伪类可以搜索这些父级标签下的全部子元素(可以嵌套)或是兄弟元素,执行:has()
内部的选择器,筛选出符合条件的父级标签
嵌套使用
:has()
选择器可以跟 :is()
、:where()
、:not()
选择器一起使用,实现更搞笑的选择,考虑下面代码:
html
<section>
<article>
<h1>Morning Times</h1>
<h2>Delivering you news every morning</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua.
</p>
</article>
<article>
<h1>Morning Times</h1>
<h2>Delivering you news every morning</h2>
<h3>8:00 am</h3>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua.
</p>
</article>
</section>
css
:is(h1, h2, h3):has(+ :is(h2, h3, h4)) {
color: red;
}
以上代码使用 :is()
和 :has()
嵌套选择,表示选择 h1、h2、h3 标签,并且从这些标签中筛选出含有直接兄弟元素为 h2、h3或 h4 的标签。一起看下最后的结果:
快看看和你自己想的结果是不是一致的!!
兼容性
兼容性不容乐观!
总结
本文主要介绍了 :is()
、:where()
、:not()
、:has()
选择器的基本使用方法和选择原理并给出了各个选择器的兼容性。
希望本文能帮助你更好的了解奇妙的 CSS 世界。