likes
comments
collection
share

el-tree中支持Shift多选时统一改变背景颜色的自定义实现

作者站长头像
站长
· 阅读数 38

背景

在搜索了半个小时后,发现并没有现成的关键词来简单实现在el-tree中用键盘shift执行多选。

于是考虑自定义实现。

上述功能有两个方面:

  1. 按住Shift键多选后,el-tree对应详情内容的选择,以执行批量操作。
  2. 按住Shift键多选后,el-tree上背景颜色的改变,以使用户感知到多选的功能。

其中,第1点极度依赖业务逻辑,但是第2点倒可以提取出来统一性的实现方式。

其难点在于:

  1. 需要使用CSS的动态绑定类选择器控制是否展示对应样式。
  2. el-tree自动生成的DOM结构是有复杂的包含结构的,怎么通过子选择器控制父选择器的样式(下文的“需求分析”有解释)。

实现代码之HTML

话不多说,实现代码粘贴如下,后跟解释。

 <el-tree
      :data="dataList"
      node-key="index"
      highlight-current
      check-on-click-node
      default-expand-all
      :props="{ label: 'name' }"
      >
  <div class="data-list" slot-scope="{ node, data }" >
    <div
         :class="{ active: data.isSelected }"
         @click="handleSelectData(data, node)"
         >
      <div class="data-list-item-content">
        <span class="data-list-item-content-icon">
          <i class = "icon"></i>
        </span>
        <span class="data-list-item-content-name">{{node.label}}</span>
      </div>
      <div class = "multiBackChange"></div>
    </div>
   </div>
</el-tree>

代码逐行解释

首先,前8行是el-tree组件的引用,使用方式可见Tree树形控件官方文档

 <div class="data-list" slot-scope="{ node, data }" >

然后,使用 scoped slot 会传入两个参数nodedata,分别表示当前节点的 Node 对象和当前节点的数据。(tree 组件依赖 data 数据,所以需要把数据从数组结构转成 tree 结构

<div 
    :class="{ active: data.isSelected }"
    @click="handleSelectData(data, node)" >

再然后,诶!到了一个精巧之处,使用isSelected这个变量值来控制是否展示active类的样式。并设置点击事件handleSelectData,在此方法中完成根据Shift键改变isSelected的值(True/False)。

<div class="data-list-item-content"> 
    <span class="data-list-item-content-icon"> 
        <i class = "icon"></i> 
    </span> 
    <span class="data-list-item-content-name">{{node.label}}</span>
</div>

此处的内容为每一个树节点所需要展示的内容,不必过多解释。

<div class = "multiBackChange"></div>

那么问题来了,这里为什么需要加这么一个只有class的div

需求分析

还记得我们的需求吗?我们需要的是当Shift执行多选时,已选中的内容的背景颜色要改变。

  1. 假如没有特定背景色改变的需求,那么el-tree中有默认的单独选中的背景色(浅灰色)。
  2. 假如没有支持Shift多选的需求,那么如下代码可修改单选的背景色。
.el-tree-node:focus > .el-tree-node__content {
    background: red
}
.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
    background: red
}
  1. 现在既有特定背景色的需求,又要支持多选。

实现思路: 我们在代码里通过绑定active类来覆盖掉el-tree中默认背景色的改变,并且支持多选。

通过上面的代码分析,active绑定在了data-list的子元素内,如果只是设置active的背景色,下图红色区域的背景色并不会改变。

el-tree中支持Shift多选时统一改变背景颜色的自定义实现

然而我们想要的效果是树结构中的整个节点的背景色都改变(包括箭头区域),如下图所示。

el-tree中支持Shift多选时统一改变背景颜色的自定义实现

但是,看控制台里的el-tree的结构,发现我们要实现的功能其实是“在CSS中实现父选择器的效果”!

如何在CSS中实现父选择器效果

实现方式为:

  1. 新建一个multiBackChange元素负责外观,放置在active元素后,用absolute拉伸和el-tree-content尺寸一致。
  2. 通过相邻兄弟选择器,控制样式变化。

所以,代码如下。

<div class = "multiBackChange"></div>

<style>
.attr-list {
    ...
    z-index: 1;//这里要设置此元素在multiBackChange的上面,防止点击失效
    ...
 }
.multiBackChange {
  position : absolute;
  right: 0;
  top: 0;
  height: 35.2px;
  width: 238px;
  display: none;
  z-index: -1;
}
.active + .multiBackChange{
  display: block;
  background: rgba(44,104,255,0.1);
}
</style>

另外,别忘了重置el-tree里默认的背景颜色修改。

.el-tree-node:focus > .el-tree-node__content {
    background: white
}
.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
    background: white
}

最后,全部代码如下所示。

 <el-tree
      :data="dataList"
      node-key="index"
      highlight-current
      check-on-click-node
      default-expand-all
      :props="{ label: 'name' }"
      >
  <div class="data-list" slot-scope="{ node, data }" >
    <div
         :class="{ active: data.isSelected }"
         @click="handleSelectAttr(data, node)"
         >
      <div class="data-list-item-content">
        <span class="data-list-item-content-icon">
          <i class = "icon"></i>
        </span>
        <span class="data-list-item-content-name">{{node.label}}</span>
      </div>
      <div class = "multiBackChange"></div>
    </div>
   </div>
</el-tree>

<style>
.attr-list {
    ...
    z-index: 1;//这里要设置此元素在multiBackChange的上面,防止点击失效
    ...
 }
.multiBackChange {
  position : absolute;
  right: 0;
  top: 0;
  height: 35px;
  width: 238px;//这里的heightwidth大小都和el-tree中每个节点的大小一致
  display: none;
  z-index: -1;
}
.active + .multiBackChange{
  display: block;
  background: rgba(44,104,255,0.1);
}
//重置el-tree中默认的点击效果
.el-tree-node:focus > .el-tree-node__content {
    background: white
}
.el-tree--highlight-current .el-tree-node.is-current > .el-tree-node__content {
    background: white
}
</style>

<script>
handleSelectAttr(attr, node) {
    ...
    attr.isSelected = true;
    ...//这里的逻辑自己写吧~~~
}
</script>