关于html:纯-CSS-实现带连接线的树形组件

63次阅读

共计 4989 个字符,预计需要花费 13 分钟才能阅读完成。

之前在这篇文章(CSS 实现树状构造目录)中实现了一个树状构造,成果是这样的

整个实现没有用到任何 JavaScript,十分奇妙,有趣味能够回顾一下。

不过有时候还须要那种带连接线的款式,这样看起来层级会更清晰,就像这样

这是如何实现的呢?一起来看看吧~

一、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>

间接就实现了开展和收起

还能够反对多层嵌套,只须要将 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>

这样就失去了一个简略的树状构造

看着还不像?那是因为当初还没有缩进,能够这样

details{padding-left: 10px}

简略调整一下间距后失去这样的成果,是不是要清晰很多?

二、绘制加号和减号

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

旧版本浏览器须要通过专门的伪元素批改,::-webkit-details-marker::-moz-list-bullet ,当初都对立成了list-style

summary{list-style: none;}

当然,也能够扭转 summarydisplay属性(默认是list-item

summary{display: flex;}

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

而后,绘制加号(➕)和减号(➖),因为还有外围一个正方形边框,咱们能够用伪元素来绘制(当然,这是在能够应用的状况下),益处是能够间接用 border 画边框,这比用突变不便的多,而后加号就是两段线性突变,如下

用代码实现就是

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;
}

调整一下间距,成果如下

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

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

当初就能够辨别哪些是开展,哪些是折叠的了

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

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

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

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

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

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

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

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

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

三、绘制连接线

最初就是绘制连接线,也是 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;
}

成果如下

看着如同有些凌乱?的确有很多线是多余的,比方树的最初一个节点,垂直线段不应该持续向下延长了,最左侧的线也是多余的,上面是示意图,咱们其实想要左边那样的成果

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

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

成果如下

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

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

为了辨别,上面将这一部分用 蓝色 示意

还有一个小优化,当初最左侧第一层都是离开的,看着有些零散,这是因为后面这一步将所有最初一层的垂直线段都去掉了,所以须要还原这种状况,能够用子选择器 > 选到,如下

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

为了辨别,上面将这一部分用 紫色 示意

实线画进去了,虚线还远吗?同样也能够用突变实现,只不过须要用repeating-linear-gradient,因为虚线其实是一直反复的从实色到通明的突变,示意如下

用代码实现就是

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;
}

这样就实现了文章结尾成果了

上面是残缺 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 tree with line (codepen.io)
  • CSS tree with line (runjs.work)

四、总结一下

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

  1. detailssummary 原生反对开展收起
  2. detailssummary 反对多层嵌套,这样就失去了繁难的树状构造
  3. 逐层缩进能够通过给 details 增加内边距实现
  4. summary 的彩色三角形是通过 list-style 生成的,能够更改 display 属性去除
  5. 利用伪元素能够轻易实现 border 边框,这比用突变不便的多
  6. 加号其实是两段线性突变叠加而成,减号一段突变就够了
  7. 连接线能够分成两段,垂直线段绘制在 details 上,程度线段绘制在 summary
  8. 多余的线段能够通过 :last-child和子选择器 > 去除
  9. 虚线其实是一直反复的从实色到通明的突变,能够用 repeating-linear-gradient 绘制

相比于现有的组件库,原生实现最大的益处就是灵活性,正当使用选择器,各式各样的设计都能轻易实现,组件库可兼顾不了这么多。另外,兼容性方面也十分不错,支流浏览器均反对,IE 上尽管不反对 details 和 summary,然而通过 polyfill 解决,总的来说十分实用的,大能够放心使用。最初,如果感觉还不错,对你有帮忙的话,欢送点赞、珍藏、转发❤❤❤

正文完
 0