纯css实现树形组件样式(自用笔记)

前言

使用纯css实现树形组件,整体结构需要利用到 details和 summary,它们天然地支持内容展开和收起。

特别说明:以下所有例子代码都是在react项目中进行

以下是一个简单例子:

import React from 'react';

const Unpublished = () => {
  return (
    <div>
      <details>
        <summary>这是一台机器</summary>
        <p>这是一台机器展开了</p>
      </details>
    </div>
  );
};

export default Unpublished;

效果图:
在这里插入图片描述
在这里插入图片描述

一、支持多层级嵌套

需要将details当做展开内容即可,代码如下:

import React from 'react';

const Unpublished = () => {
  return (
    <div>
      <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>
    </div>
  );
};

export default Unpublished;

效果如下:
在这里插入图片描述
加上缩进样式:

  details {
    padding-left: 15px;
  }

加上缩进样式效果:
在这里插入图片描述

二、画出➕和➖ 符号

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

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

summary{
  list-style: none;
}

也可以改变summary的display属性(默认是list-item)

summary{
  display: flex;
}

这时,默认的黑色三角去除了,可以绘制➕和➖了
绘制加号(➕)和减号(➖),由于还有外围一个正方形边框,我们可以用伪元素来绘制(当然,这是在可以使用的情况下),好处是可以直接用border画边框,这比用渐变方便的多,然后加号就是两段线性渐变,如下
在这里插入图片描述
css代码如下:

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的情况*/
}

效果如下:
在这里插入图片描述

三、画连接线

可以分解开来处理,一部分是垂直的,指向树的每个标题部分,所以直接绘制在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%;
}

在这里插入图片描述
将实线改成虚线

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

效果图:
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这个问题涉及到前端开发领域的知识,我会尽力回答。 对于HTML树形菜单的折叠,可以使用CSS中的伪类选择器`:checked`和`+`(相邻兄弟选择器)来实现。具体操作步骤如下: 1. 在HTML中使用input type="checkbox"元素来表示菜单项是否展开或折叠: ``` <div class="menu"> <input type="checkbox" id="item1"> <label for="item1">菜单项1</label> <ul> <li>子菜单项1</li> <li>子菜单项2</li> </ul> </div> ``` 2. 使用CSS设置菜单项的样式,并使用`:checked`和`+`选择器来控制子菜单的展开或折叠: ``` .menu ul { display: none; /* 初始状态下子菜单项不显示 */ } .menu input[type="checkbox"]:checked + label + ul { display: block; /* 当菜单项的复选框被选中时,显示子菜单项 */ } ``` 至于目录树折叠菜单jquery版(带复选框),可以使用jQuery来实现。具体操作步骤如下: 1. 在HTML中使用ul和li元素来表示目录树的结构: ``` <ul class="tree"> <li> <input type="checkbox" id="item1"> <label for="item1">节点1</label> <ul> <li>子节点1</li> <li>子节点2</li> </ul> </li> <li> <input type="checkbox" id="item2"> <label for="item2">节点2</label> <ul> <li>子节点1</li> <li>子节点2</li> </ul> </li> </ul> ``` 2. 使用jQuery来绑定复选框的change事件,并根据复选框的状态来展开或折叠子节点: ``` $(function() { // 绑定复选框的change事件 $('.tree input[type="checkbox"]').change(function() { if ($(this).is(':checked')) { // 当复选框被选中时,展开子节点 $(this).siblings('ul').slideDown(); } else { // 当复选框未选中时,折叠子节点 $(this).siblings('ul').slideUp(); } }); }); ``` 以上就是实现HTML树形菜单的折叠和目录树折叠菜单jquery版的基本操作步骤。希望能对你有所帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值