css 中那些好用的伪类选择器

329 阅读4分钟

本篇文章收集了几个工作或学习时用到的,好用但是并不常见的 css 伪类选择器,在此做个分享。今后遇到了别的类似选择器,会持续更新~

:has()

在 css 中,如果想根据父元素来选择子元素,我们可以使用 :nth-child() 这样的伪类选择器。如果想根据子元素来选择父元素时,目前并没有个 :parent() 这样的选择器,但是可以使用 :has() 来实现我们的目的。

案例

举个例子,我在一个后台项目中大量的使用了 element ui 的 Table 表格组件 ,几乎每个表格都会有子元素为按钮(button)的操作列,我想统一地设置这些按钮的父元素的样式为 display: flex;,然后通过 gap 属性让存在多个按钮时,每个按钮之间能保持间距,并且通过 flex-wrap: wrap; 使得当按钮过多的时候还能自动换行。

通过浏览器查看 Table 表格组件的元素,可以看到表格的每一个包含数据的单元格 <td> 都有个类名 el-table__cell,它还有个子元素 —— <div class="cell"> —— 我们自己定义的表格内容就会包裹在里面:

2.png

也就是说我们需要找到子元素中有 button.cell,然后给它设置样式,此时就用到了 :has()

.el-table__cell > .cell:has(button) {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  * {
    margin-left: 0 !important;
    margin-right: 0 !important;
  }
}

.el-table__cell > .cell:has(button) 的意思就是找到 .cell,它紧挨着的父元素是 .el-table__cell,并且它的子元素中有 button。呈现的效果如下:

1.png

传递给 :has() 的值是一个可容错相对选择器列表,也就是可以有多个选择器,彼此用 , 分割,且当有某个选择器无效时会被直接忽略,不影响其它选择器的正常使用。比如有下面 4 个元素:

<p>我是p1</p>
<div>我是div1</div>
<div>我是div2</div>
<p>我是p2</p>
<div>我是div3</div>

想找到同一个父级元素下的在 p 元素前面的所有 div,让它们的字体颜色变红:

div:has(~ p) {
  color: red;
}

效果如下:

image.png

兼容性

请注意 :has 的兼容性:

3.png

在微信小程序中是不兼容的,我一般只在后台项目中使用。

:is() 与 :where()

案例

在实际项目中我还没使用过 :is():where(),就先举个如下的例子:

<div class="d1">
  <span>你好</span>
</div>
<div class="d2">
  <span>掘金</span>
</div>
<div class="d3">
  <span>周末愉快</span>
</div>

假设有 3 个 div,我想让 .d1.d2 里的 span 字体颜色为红色,我们可以这样写:

/* 代码片段 1 */
.d1 span,
.d2 span {
  color: red;
}

也可以使用 :is():where() 使得代码看起来更紧凑:

:is(.d1, .d2) span {
  color: red;
}

/* 或者 */
:where(.d1, .d2) span {
  color: red;
}

:is():where() 接收的参数为容错选择器列表,这就意味着即使我们传递的某个选择器无效甚至是错误的,比如像下面这样 :hover 少写了一个 r,也只是忽略掉该不正确的选择器而不会影响其它选择器:

:is(.d1:hove, .d2) span {
  color: red;
}

效果如下:

4.png

但如果是代码片段 1 那样的写法:

.d1:hove span,
.d2 span {
  color: red;
}

当写错一个选择器,整个选择器列表都会失效:

5.png

:is() 与 :where() 的区别

:is():where() 大部分情况下使用的效果都相同,区别在于优先级。:is() 会选择其参数中优先级别最高的那个选择器的权重作为自己的权重,而 :where() 的优先级为 0。所以如果 css 样式如下:

.d1 span,
.d2 span {
  color: red;
}
:is(.d1) span {
  color: green;
}
:where(.d2) span {
  color: yellow;
}

最后呈现的效果如下:

6.png

因为 :is(.d1) span 的权重与 .d1 span 相同,:is(.d1) span 写在后面,故优先级更高,"你好" 便是绿色;:where(.d2) span 的权重相当于一个单独的 span,低于 .d2 span,所以 "掘金" 是红色。

兼容性

:is():where() 的兼容性比 :has() 更好,现代浏览器基本上都可以使用。

:empty

:empty 用于选择不包含任何子元素的元素。

案例

在使用原生语法开发微信小程序时,组件的插槽没有提供类似于 vue 中的默认值功能。想实现插槽默认内容的功能,我们可以在定义子组件时,给 <slot> 包裹一个 <view>,添加类名为 slot-wrap,然后紧接着定义一个类名为 slot-default<view> 来声明插槽的默认内容:

<view>
  <view>我是子组件</view>
  <view class="slot-wrap">
    <slot>默认内容,小程序中不生效</slot>
  </view>
  <view class="slot-default">默认内容</view>
</view>

在组件的样式文件中,就可以先让 .slot-default 默认不显示,然后在 .slot-wrap:empty 即插槽无子元素时,也就是使用组件时没有传入插槽内容的时候,让 .slot-default 显示:

.slot-default {
  display: none;
}

.slot-wrap:empty+.slot-default {
  display: block;
}

兼容性

现代浏览器基本上都可以使用。

感谢.gif 点赞.png