纯 CSS 实现带连接线的树形组件

一、details 和 summary

简单回顾一下,整体结构需要利用到 details 和 summary,天然地支持内容展开和收起。这里有一个 MDN 的例子

<details>
  <summary>System Requirements</summary>
  <p>Requires a computer running an operating system. The computer
  must have some memory and ideally some kind of long-term storage.
  An input device as well as some form of output device is
  recommended.</p>
</details>

直接就实现了展开和收起

Kapture 2022-04-10 at 18.09.18.gif

还可以支持多层嵌套,只需要将details当做展开的内容就行了,如下

<details>
  <summary>项目1</summary>
  <details>
    <summary>文件夹0</summary>
  </details>
  <details>
    <summary>文件夹1-1</summary>
    <details>
      <summary>文件夹1-1-2</summary>
    </details>
    <details>
      <summary>文件夹1-1-3</summary>
      ...
  </details>
</details>

这样就得到了一个简单的树状结构

Kapture 2022-04-10 at 18.24.16.gif

看着还不像?那是因为现在还没有缩进,可以这样

details{
  padding-left: 10px
}

简单调整一下间距后得到这样的效果,是不是要清晰很多?

image.png

二、绘制加号和减号

首先,默认的黑色三角太丑了,需要去掉。现代浏览器中,这个“黑色三角”其实是 ::marker生成的,而这个 ::marker是通过list-style生成,所以要去除就很简单了

旧版本浏览器需要通过专门的伪元素修改, ::-webkit-details-marker::-moz-list-bullet ,现在都统一成了 list-style
summary{
  list-style: none;
}

当然,也可以改变summarydisplay属性(默认是list-item

summary{
  display: flex;
}

这样,默认的三角就去除了

image-20230629162539762

然后,绘制加号(➕)和减号(➖),由于还有外围一个正方形边框,我们可以用伪元素来绘制(当然,这是在可以使用的情况下),好处是可以直接用border画边框,这比用渐变方便的多,然后加号就是两段线性渐变,如下

image.png

用代码实现就是

summary::before{
    content: '';
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    margin-right: 8px;
    border: 1px solid #999;
    background: linear-gradient(#999, #999) 50%/1px 10px no-repeat,linear-gradient(#999, #999)  50%/10px 1px no-repeat;
}

调整一下间距,效果如下

image.png

现在都是加号(➕),看不出哪些是展开的,所以还需要绘制减号(➖),可以用[open]属性来判断,相较于加号(➕)而言,只需要一个线性渐变就行了,实现如下

details[open]>summary::before{
    background: linear-gradient(#999, #999) 50%/10px 1px no-repeat;
}

现在就可以区分哪些是展开,哪些是折叠的了

image.png

到了这一步,其实还有一个小问题,有些是不能继续展开的,因为已经到了最底层,没有内容了,所以希望在没有展开内容的时候不显示加号(➕)或者减号(➖),这应该如何判断呢?

其实很简单,在没有展开内容的情况下,其实只有summary单个标签,就像这种结构

<details>
  <summary>文件</summary>
  <!--没有内容了-->
</details>

提到单个标签,可以想到:only-child伪类,所以可以这样重置一下

summary:only-child::before{
  display: none
}

还有另外一种做法,那就是借助:not伪类,直接在前面的选择器上加一层判断

summary:not(:only-child)::before{
    /*排除单个summary的情况*/
}

这样会更加优雅~效果如下

image.png

这样就能轻易的看出哪些是不能展开的了

三、绘制连接线

最后就是绘制连接线,也是 CSS 最灵活的、最有趣的一部分。

先从绘制实线开始,这样比较容易。

直接绘制可能有些难度,我们可以分解开来,一部分是垂直的,指向树的每个标题部分,所以直接绘制在summary上,还有一部分是竖直的,并且竖直部分会包含整个展开部分,因此可以把线条绘制在details上,用代码实现如下(为了区分,下面把垂直部分用红色表示)

summary{
  /*水平线*/
  background: linear-gradient(#999,#999) 0px 50%/20px 1px no-repeat;
}
details{
  /*垂直线*/
  background: linear-gradient(#999, #999) 40px 0px/1px 100% no-repeat;
}

效果如下

image.png

看着好像有些凌乱?确实有很多线是多余的,比如树的最后一个节点,垂直线段不应该继续向下延伸了,最左侧的线也是多余的,下面是示意图,我们其实想要右边那样的效果

image.png

首先是最左侧的线段,其实就是最外层,也就是第一层,要去除很简单,直接选中第一层的details以及下面的summary就行了,这里可以用子选择器>来实现

.tree>details,
.tree>details>summary{
  /*去除最外层的连接线*/
  background: none
}

效果如下

image.png

然后就是每层的最后一个子节点,如何将垂直线段去除呢?其实可以从HTML结构上入手,最后一层,其实就是最后一个details,所以将最后一个的背景尺寸改为刚好和垂直线段吻合

details:last-child{
  background-size: 1px 23px;
}

为了区分,下面将这一部分用蓝色表示

image.png

还有一个小优化,现在最左侧第一层都是分开的,看着有些零散,这是因为前面这一步将所有最后一层的垂直线段都去掉了,所以需要还原这种情况,可以用子选择器>选到,如下

.tree>details:not(:last-child)>details:last-child{
   background-size: 1px 100%;
}

为了区分,下面将这一部分用紫色表示

image.png

实线画出来了,虚线还远吗?同样也可以用渐变实现,只不过需要用repeating-linear-gradient,因为虚线其实是不断重复的从实色到透明的渐变,示意如下

image.png

用代码实现就是

summary{
  /*水平虚线*/
  background: repeating-linear-gradient( 90deg, #999 0 1px,transparent 0px 2px) 0px 50%/20px 1px no-repeat;
}
details{
  /*垂直虚线*/
  background: repeating-linear-gradient( #999 0 1px,transparent 0px 2px) 40px 0px/1px 100% no-repeat;
}

这样就实现了文章开头效果了

Kapture 2023-06-28 at 19.47.32.gif

下面是完整CSS代码(真的不多了)

.tree summary{
    outline: 0;
    padding-left: 30px;
    list-style: none;
    background: repeating-linear-gradient( 90deg, #999 0 1px,transparent 0px 2px) 0px 50%/20px 1px no-repeat;
    /* background: linear-gradient(#999,#999) 0px 50%/20px 1px no-repeat; */
}
.tree details:last-child{
    background-size: 1px 23px;
}
.tree>details:not(:last-child)>details:last-child{
    background-size: 1px 100%;
}
.tree details{
    padding-left: 40px;
    background: repeating-linear-gradient( #999 0 1px,transparent 0px 2px) 40px 0px/1px 100% no-repeat;
    /* background: linear-gradient(#999, #999) 40px 0px/1px 100% no-repeat; */
}
.tree>details{
    background: none;
    padding-left: 0;
}
.tree>details>summary{
    background: none
}
.tree summary{
    display: flex;
    align-items: center;
    height: 46px;
    font-size: 15px;
    line-height: 22px;
    color: rgba(0, 0, 0, 0.85);
    cursor: default;
}
.tree summary::after{
    content: '';
    position: absolute;
    left: 10px;
    right: 10px;
    height: 38px;
    background: #EEF2FF;
    border-radius: 8px;
    z-index: -1;
    opacity: 0;
    transition: .2s;
}
.tree summary:hover::after{
    opacity: 1;
}
.tree summary:not(:only-child)::before{
    content: '';
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    margin-right: 8px;
    border: 1px solid #999;
    background: linear-gradient(#999, #999) 50%/1px 10px no-repeat,linear-gradient(#999, #999)  50%/10px 1px no-repeat;
}
.tree details[open]>summary::before{
    background: linear-gradient(#999, #999) 50%/10px 1px no-repeat;
}

你也可以查看以下任意链接:

四、总结一下

以上就是本文的全部内容了,可以看到全部由 CSS 绘制而成,没有用到任何图片,是不是很简单呢?下面总结一下实现要点

  1. details 和 summary 原生支持展开收起
  2. details 和 summary 支持多层嵌套,这样就得到了简易的树状结构
  3. 逐层缩进可以通过给 details 添加内边距实现
  4. summary 的黑色三角形是通过 list-style 生成的,可以更改 display 属性去除
  5. 利用伪元素可以轻易实现 border 边框,这比用渐变方便的多
  6. 加号其实是两段线性渐变叠加而成,减号一段渐变就够了
  7. 连接线可以分成两段,垂直线段绘制在 details 上,水平线段绘制在 summary 上
  8. 多余的线段可以通过 :last-child和子选择器>去除
  9. 虚线其实是不断重复的从实色到透明的渐变,可以用repeating-linear-gradient绘制

相比于现有的组件库,原生实现最大的好处就是灵活性,合理运用选择器,各式各样的设计都能轻易实现,组件库可兼顾不了这么多。另外,兼容性方面也非常不错,主流浏览器均支持,IE 上虽然不支持 details 和 summary,但是通过 polyfill 解决,总的来说非常实用的,大可以放心使用。

### 回答1: layui下拉树形组件是一款基于layui框架开发的UI组件,可以将数据以树形展示并以下拉列表的形式呈现。该组件拥有简单易用的特点,能够满足页面交互中对树形结构需求的展示。 使用layui下拉树形组件,首先需要引入相关的CSS和JS文件,并初始化一个下拉树形组件的容器。然后,通过调用layui的相关方法,将数据以树形结构的形式渲染到容器中。下拉树形组件支持自定义配置,可以通过配置项设置展开、折叠图标的样式、选中节点的回调函数等。 在使用过程中,可以通过调用相关的方法对下拉树形组件进行操作,比如获取选中的节点、展开或折叠某个节点等。下拉树形组件支持多级别的树形结构,并且可以进行异步加载数据,提供了丰富的API方法供开发者使用。 总之,layui下拉树形组件是一个方便易用的UI组件,在前端开发中广泛应用于树形结构的展示与选择。无论是在后台管理系统还是前端开发中,都具有很好的适用性。 ### 回答2: layui下拉树形组件是一种基于layui前端框架开发的功能强大、使用方便的下拉菜单组件。它可以用于展示树形结构数据,并支持用户选择节点。 在使用过程中,我们首先需要引入layui的相关资源文件,并在页面中创建一个select元素作为下拉树形组件的容器。然后,通过调用layui的下拉树形组件方法,传入相关参数,即可将数据渲染成树形结构显示在下拉框中。 该组件支持使用JSON格式的数据源,并且可以自定义节点的显示样式、选中时的样式,以及节点之间的连接线样式。还可以设置下拉框的宽度、最大高度,以及是否显示搜索框等。 在使用过程中,我们可以通过监听下拉树形组件的选择事件,获取用户选择的节点信息,然后进行相应的操作。另外,该组件还支持节点的展开与折叠操作,以便用户可以方便地查看和选择节点。 总之,layui下拉树形组件是一款强大而实用的前端组件,它为开发者提供了方便的树形展示和选择功能,可以广泛应用于各种Web应用中。无论是在企业管理系统还是电商平台中,都可以通过使用layui下拉树形组件来提升用户体验,优化界面交互。 ### 回答3: layui是一个基于jQuery的前端UI框架,提供了一系列的常用UI组件。在layui中,下拉树形组件用于展示树形结构的数据,并且支持展开和收起节点,方便用户选择。 使用layui下拉树形组件的步骤如下: 1. 导入layui的相关文件,包括css和JavaScript文件。 2. 在页面中添加一个下拉选择框的HTML元素。 3. 在JavaScript代码中,使用layui的form模块进行表单渲染。 4. 使用layui的tree模块创建一个树形结构。 5. 通过AJAX请求获取树形结构的数据,并将数据渲染到tree组件中。 6. 设置下拉选择框的触发事件,当用户点击下拉框时,展示树形结构。 7. 当用户选择节点时,使用事件监听器监听选择事件,并将选中的节点值设置到下拉框中。 8. 用户提交表单时,获取下拉框中选中的节点值,进行相应的处理。 需要注意的是,使用layui下拉树形组件时,需要对数据进行适当的格式化,以满足组件的要求。例如,数据需要是一个数组,每个节点需要包含id和name等属性。同时,需要根据实际需求,设置组件的参数,如展开节点的深度、是否显示复选框等。 总结来说,layui下拉树形组件能够方便地展示树形结构的数据,并提供了丰富的功能和配置选项,可以满足不同场景下的需求。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值