Skip to content

Css has 选择器

has 选择器是什么?

CSS 中的 "has 选择器" 是 CSS 级联样式表的一种选择器,用于选择含有满足指定选择器的子元素的父元素。它使用 :has() 伪类来实现。

":has()" 伪类接受一个选择器作为参数,表示要选择包含满足该选择器的子元素的父元素。它在 CSS 选择器中常用于在父元素中选择特定的子元素。用更为直白的话来说,它是一个条件满足的情况下,能够子改父 | 父辈的选择。

它的用法也相当的简单,比如现在要针对只要子元素含有child类的parent类更改背景色,就可以如下:

css
.parent:has(.child) {
  /* 对含有特定子元素的父元素应用样式 */
  background-color: red;
}
.parent:has(.child) {
  /* 对含有特定子元素的父元素应用样式 */
  background-color: red;
}

咋一眼看起来这个选择器平淡无奇, 但其实它可以实现非常实用的样式效果,比如说聚焦悬浮列表以及一些骚操作等。

聚焦列表

当观察表格的时候,如果行和列过于多的时候,为了提醒以及方便用户观察表格数据,一般的 UI 表格组件都会在:hover行的时候给予当前悬浮行一个背景颜色。

但万一行数据量十分的爆炸(动不动百|千万量级别数据量,30+列的需求我都麻木了),而且单元格的间距设置的密密麻麻呢? ok 这个时候过于追求再添加背景色等一系列手段效果也会逐渐降低,悬浮是一个不错的方案,但悬浮也不是一个最有效果的效果。 那最有效果的效果是什么? 看不见 不就是最有效的效果吗。

ok,这里就可以完美的使用has的选择器的时机了,当然这个还需要借助相邻兄弟选择器。拿 el-table 举例;

css
.el-table__row:has(+ tr:hover),
.el-table__row:hover + .el-table__row {
  filter: blur(2px);
}
.el-table__row:has(+ tr:hover),
.el-table__row:hover + .el-table__row {
  filter: blur(2px);
}
  • .el-table__row:has(+ tr:hover):选择拥有下一个兄弟元素处于:hover 状态的.el-table__row 元素。也就是当兄弟元素行处于鼠标悬停状态时,选择当前行。
  • .el-table__row:hover+.el-table__row:选择当前行的兄弟元素行。也就是当前行的下一个兄弟元素行。

最后让它们都赋予filter(滤镜)就可以达到“看不见”的最佳聚焦效果,

聚焦效果

复选 el-tree 组件强行改单选组件

相信前端都用过el-tree这个树形组件,它是用于展示和选择用途,但它可惜只能多选。 但是现在产品需求中要求类似树状展开,同时点击某个节点即位选中,以及如果叶子节点不满足条件的时候点击不可以选中。 这个需求一点都不过分,因为树状展开确实比下拉框有更好的可视关系,但el-tree是多选呀,代表了不能使用v-model来实现需求。 当前面临两个方向解决这个问题:

  • 自己搓一个下拉树组件,单带选择的功能, 可是时间工期不允许,时间短未必实现有el-tree那么稳定和流畅(~~血与泪经历,之前单搓自研了一个 colorPicker ~~)
  • 说服产品改为下拉树状选择器 (摆烂)

后面认真观察发现,el-tree在点击节点的时候,会默认给当前选中节点一个背景色,提示用户它在这里选中了展开下面的节点或者点击该节点(同时回调函数抛出当前选中节点数据)。 那其实把它能成选中不就可以吗? 只需要考虑如何告诉节点它是不能点击的,哪怕被点击了也要伪装不就可以?

Alt text

沿着这个思路往下发散,因为数据层面上,肯定是能知道什么节点的能点击,什么节点不能点击 (在数据层面上植入了disabled, true 代表不能点击,反之则能点击), 然后再观察el-tree输出的 DOM 结构,可以观察是一个行(它的类为el-tree-node__content)包裹了在代码中写的插槽(<template #default="{ node, data }"></template>)的html内容,它们属于父子关系。

那么就可以在内容上,根据数据判断能不能点输入一个类,举个例子比如:

vue
<el-tree>
 <template #default="{ node, data }">
     <div :class="data.disabled ? 'isDisabled' : ''" ></div>
 </template>
</el-tree>
<el-tree>
 <template #default="{ node, data }">
     <div :class="data.disabled ? 'isDisabled' : ''" ></div>
 </template>
</el-tree>

然后再使用:has,伪装当前效果(当前它还是会抛出事件,但那个好控制,可以根据标识判断要不要给上层组件),

css
.el-tree-node__content:has(.isDisabled) {
  background: #fff !important;
  color: #a8abb2 !important;
  cursor: no-drop !important;
}
.el-tree-node__content:has(.isDisabled) {
  background: #fff !important;
  color: #a8abb2 !important;
  cursor: no-drop !important;
}

这样就可以设置了只要节点含有isDisabled类(不能点击),当点击后样式就不会发生变化,同时cursor: no-drop 也让用户悬浮在当前节点上呈不可点击手势图标(视觉上)。 当然的它点击还是会抛出数据,不过既然有标识,那么拦截它不让该数据往上层传递就非常简单的事情了。

treshascss