树状菜单(利用映射-bootstrap+jQuery实现折叠功能)

news/2024/7/11 0:00:32 标签: bootstrap, jquery, 前端

效果(默认全部展开):
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link
      rel="stylesheet"
      href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css"
    />
    <script src="https://cdn.staticfile.org/jquery/2.1.1/jquery.min.js"></script>
    <script src="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script>
  </head>
  <style>
    /* 树状菜单 */
    #ulShow {
      height: 100%;
    }

    #ulShow > .list-group {
      color: #fff;
      margin: 0;
      height: 40px;
      line-height: 40px;
      margin: 0;
      list-style: none;
    }

    /* 一级菜单 */
    #ulShow > .list-group > .list-group {
      margin: 0;
      padding-left: 20px;
      background-color: #2d363f;
    }

    #ulShow > .list-group > .collapse > .list-group {
      margin: 0;
    }

    /* 动画进行时的样式设置,要和动画结束时的样式保持一致 */
    #ulShow > .list-group > .collapse > .list-group > .list-group-item,
    #ulShow > .list-group .collapsing .list-group-item {
      display: flex;
      align-items: center;
      padding: 0;
      padding-left: 40px;
      background-color: #1e2d3b;
      border: 0;
    }

    #ulShow > .list-group > .collapse > .list-group > .list-group-item a,
    #ulShow > .list-group .collapsing .list-group-item a {
      text-decoration: none;
      color: #fff;
    }

    #ulShow > .list-group > .collapse > .list-group > .list-group-item:hover,
    #ulShow > .list-group .collapsing .list-group-item:hover {
      background: #2d363f;
      cursor: pointer;
    }

    #ulShow > .list-group > .collapse > .list-group > .list-group-item:hover a,
    #ulShow > .list-group .collapsing .list-group-item:hover a {
      color: lightblue;
    }

    #ulShow > .list-group .collapsing {
      transition: height 0.5s ease;
      /* 修改为你想要的持续时间和缓动函数 */
      /* 动画加载时会有一个向下的高度占位 */
      /* background-color: red; */
    }
  </style>
  <body>
    <div id="ulShow"></div>
    <script>
      const menuArray = [
        { id: 1, pageName: "用户管理", pid: 0 },
        { id: 2, pageName: "图书管理", pid: 0 },
        { id: 3, pageName: "销售管理", pid: 0 },
        { id: 4, pageName: "借阅管理", pid: 0 },
        { id: 5, pageName: "系统设置", pid: 0 },
        { id: 6, pageName: "用户类型管理", pid: 1 },
        { id: 7, pageName: "用户信息管理", pid: 1 },
        { id: 8, pageName: "新增用户", pid: 1 },
        { id: 9, pageName: "图书类型管理", pid: 2 },
        { id: 10, pageName: "图书管理", pid: 2 },
        { id: 11, pageName: "入库管理", pid: 3 },
        { id: 12, pageName: "出库管理", pid: 3 },
        { id: 13, pageName: "借书管理", pid: 4 },
        { id: 14, pageName: "还书管理", pid: 4 },
        { id: 15, pageName: "退出", pid: 5 },
      ];
      function showMenu(menuArray) {
        // 创建一个映射对象,用于存储菜单项及其子菜单项,map是计算结束后返回的值,是下一次调用回调时的第一个参数;item是当前值
        const itemMap = menuArray.reduce((map, item) => {
          // 将每个菜单项添加到映射中,并初始化其子菜单项数组
          /*
			在这段代码中,...item 用于对象展开(Object Spreading)是有效的,因为它正确地复制了 item 对象的所有可枚举属性到新创建的对象中。
			对象展开(Object Spreading)与浅拷贝(Shallow Copy)是相关的概念。在这段代码中,...item 进行的操作实际上是一个浅拷贝。
			浅拷贝是指创建一个新对象,并将原始对象的所有非静态属性的值复制到新对象中。如果这些属性值是基本类型(如数字、字符串、布尔值),则直接复制值;如果属性值是引用类型(如对象、数组),则复制的是内存中的地址,而不是实际的对象或数组。这意味着新对象和原始对象仍然共享对这些引用类型属性的引用。
			在对象展开中,...item 创建了一个新对象,并将 item 的所有可枚举属性复制到新对象中。由于这些属性是按值复制的,如果属性是基本类型,它们将被直接复制;如果属性是引用类型(例如,另一个对象或数组),则复制的是对这个引用类型值的引用,而不是实际的对象或数组本身。因此,对象展开在这种情况下执行的是一个浅拷贝。
			这段代码中的 subItems: [] 创建了一个新的空数组,并将其赋值给新对象的 subItems 属性。这是对新数组的一个独立引用,它不会影响原始 item 对象中的任何属性或数组。
			因此,当你在这段代码中使用 ...item 时,你正在创建一个新对象,该对象包含原始 item 对象的所有属性的浅拷贝,并且添加了一个新的 subItems 属性,该属性指向一个全新的数组。这样,每个 item 在 itemMap 中都有一个独立的、不与其他 item 共享的 subItems 数组。
		  */
          map[item.id] = { ...item, subItems: [] };

          // 如果菜单项没有父级(顶级菜单项),则标记为顶级
          if (item.pid === 0) {
            map[item.id].topLevel = true;
          } else {
            // 如果菜单项有父级,则将其添加到父级菜单项的子菜单项数组中
            const parentId = item.pid;
            if (map[parentId]) {
              map[parentId].subItems.push(item);
            }
          }

          return map;
        }, {});
        console.log(itemMap, "itemMap");

        // 从映射中提取所有顶级菜单项,返回一个新数组
        const topLevelItems = Object.values(itemMap).filter(
          (item) => item.topLevel
        );

        // 构建顶级菜单项的 HTML 内容
        const listGroupContent = topLevelItems
          .map((topItem) => {
            let subItemsHtml = "";

            // 如果顶级菜单项有子菜单项,则构建子菜单项的 HTML 内容
            if (topItem.subItems.length > 0) {
              // 为子菜单项创建一个唯一的 collapse ID
              const collapseId = `collapse-${topItem.id}`;

              // 构建子菜单项的列表组,并将其包装在一个 collapse 元素中
              subItemsHtml =
                `<div class="collapse" id="${collapseId}">` +
                `<ul class="list-group list-group-flush">` +
                topItem.subItems
                  .map(
                    (subItem) =>
                      // 将子菜单项转换为 a 标签,并设置 href 属性
                      `<li class="list-group-item subItem" οnclick="handleSubItem('${subItem.pageName}')"><a href="#">${subItem.pageName}</a></li>`
                  )
                  .join("") +
                `</ul>` +
                `</div>`;

              // 创建折叠触发器的按钮,并设置 data-target 属性以指向相应的 collapse 元素
              const triggerButton = `<div class="list-group"  data-toggle="collapse" data-target="#${collapseId}" aria-expanded="false" aria-controls="${collapseId}">${topItem.pageName}</div>`;

              // 返回顶级菜单项的 HTML 内容,包括折叠触发器和子菜单项
              return triggerButton + subItemsHtml;
            } else {
              // 如果顶级菜单项没有子菜单项,则只创建一个 a 标签
              return `<li class="list-group-item"><a href="#">${topItem.pageName}</a></li>`;
            }
          })
          .join("");

        // 清空 ulShow 元素的内容,并添加新构建的列表组内容
        $("#ulShow")
          .empty()
          .append(`<ul class="list-group">${listGroupContent}</ul>`);

        // 初始化所有 collapse 元素以启用折叠功能
        $(".collapse").collapse();
      }

      function handleSubItem(pageName) {
        console.log(pageName, "pageName");
      }

      $(".subItem").on("click", function (pageName) {
        handleSubItem(pageName);
      });
      window.onload = function () {
        showMenu(menuArray);
      };
    </script>
  </body>
</html>


http://www.niftyadmin.cn/n/5375004.html

相关文章

联想thinkpad-E450双系统升级记

早期笔记本联想thinkpad-E450双系统 大约16年花4000多大洋&#xff0c;买了一台thinkpad-E450屏幕是16寸本&#xff0c;有AMD独立显卡&#xff0c;i5cpu&#xff0c;4G内存。 . 后来加了一个同型号4G内存组成双通道&#xff0c; . 加了一个三星固态500G&#xff0c; . 换了一个…

基于大语言模型的AI Agents

代理&#xff08;Agent&#xff09;指能自主感知环境并采取行动实现目标的智能体。基于大语言模型&#xff08;LLM&#xff09;的 AI Agent 利用 LLM 进行记忆检索、决策推理和行动顺序选择等&#xff0c;把Agent的智能程度提升到了新的高度。LLM驱动的Agent具体是怎么做的呢&a…

【剪映】为什么要做音乐版权校验?

为什么要做音乐版权校验&#xff1f; 剪映模板开通同步抖音影集功能后&#xff0c;为了尊重原创音乐以及规避侵权风险&#xff0c;添加背景音乐时&#xff0c;建议您通过版权校验&#xff0c;在确定抖音拥有该音乐的版权后&#xff0c;此模板才可能会被同步抖音影集。

【c++基础】国王的魔镜

说明 国王有一个魔镜&#xff0c;可以把任何接触镜面的东西变成原来的两倍——只是&#xff0c;因为是镜子嘛&#xff0c;增加的那部分是反的。 比如一条项链&#xff0c;我们用AB来表示&#xff0c;不同的字母表示不同颜色的珍珠。如果把B端接触镜面的话&#xff0c;魔镜会把…

Python中Pymysql库的常见用法和代码示例

关注B站可以观看更多实战教学视频&#xff1a;肆十二-的个人空间-肆十二-个人主页-哔哩哔哩视频 (bilibili.com) pymysql是一个用于连接MySQL数据库的Python库&#xff0c;它允许你执行SQL查询并处理返回的结果。以下是pymysql库的一些常见用法和代码示例&#xff1a; 1. 安装…

16 亚稳态原理和解决方案

1. 亚稳态原理 亚稳态是指触发器无法在某个规定的时间段内到达一个可以确认的状态。在同步系统中&#xff0c;输入总是与时钟同步&#xff0c;因此寄存器的setup time和hold time是满足的&#xff0c;一般情况下是不会发生亚稳态情况的。在异步信号采集中&#xff0c;由于异步…

自动驾驶轨迹规划之kinodynamic planning

欢迎大家关注我的B站&#xff1a; 偷吃薯片的Zheng同学的个人空间-偷吃薯片的Zheng同学个人主页-哔哩哔哩视频 (bilibili.com) 本文PPT来自深蓝学院《移动机器人的运动规划》 目录 1.kinodynamic的背景 2. old-school pipline 3.example 1.kinodynamic的背景 kinodynami…

常见的物联网操作系统介绍

物联网&#xff08;Internet of Things&#xff0c;IoT&#xff09;是指将各种物理设备、车辆、家用电器、工业设备等通过网络连接起来&#xff0c;实现数据交换和通信的技术。物联网操作系统是管理这些设备并使其能够相互通信的软件平台。以下是一些常见的物联网操作系统&…