js递归实现的一个垂直菜单(可配置且美观)

该博客介绍了一个使用JavaScript递归实现的垂直菜单,适用于网页布局。菜单数据以JSON格式提供,支持点击事件回调,可通过引入jQuery库进行使用。提供了两种使用方式:通过script标签或import导入。此外,还详细解释了菜单的生成逻辑,包括样式注入和事件绑定。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

递归实现的一个垂直菜单

演示地址

演示地址:https://zl-fire.github.io/code/zl-ver-menu.html
github仓库地址:https://github.com/zl-fire/zl-ver-menu  (欢迎start)

使用前提

在使用前需要引入 jQuery,如

<script src="https://cdn.jsdelivr.net/npm/blogzl-indexjs@18.0.0/dist/jquery.min.js"></script>

使用方法

方式一:使用 script 标签引入进行使用

<!DOCTYPE html>
<html lang="zh-cn">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>zl-ver-menu使用演示</title>
    <script src="https://cdn.jsdelivr.net/npm/blogzl-indexjs@18.0.0/dist/jquery.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/zl-ver-menu@1.0.5/index.js"></script>
</head>

<body>
    <div class="your-menu"></div>
    <script>
        zl_ver_menu = window["zl-ver-menu"]; //从window上获取zl-ver-menu方法
        //准备cata数据
        let dataList = [
            {
                "id": 6,
                "parent_id": 0,
                "name": "李冰有限公司A",
                "children": [
                    {
                        "id": 7,
                        "parent_id": 6,
                        "name": "财务部",
                        "children": []
                    },
                    {
                        "id": 108,
                        "parent_id": 6,
                        "name": "第二个部门",
                        "children": []
                    }
                ]
            }
        ];
        //调用此方法生成垂直菜单(class名和上面html元素的class名一致即可,可以任取)
        zl_ver_menu({
            data:dataList, //菜单数据
            menuClassName: "your-menu", //菜单容器元素的class名
            callback: function (par) { console.log(par) }, //点击具体的菜单项后要执行的函数(par默认会传入点击的节点)
            show: true //此垂直菜单默认为全部展开
        });
    </script>
</body>

</html>

方式二:使用 import 标签引入进行使用

     import zl_ver_menu from "zl-ver-menu";
       //准备cata数据
       let dataList = [
            {
                "id": 6,
                "parent_id": 0,
                "name": "李冰有限公司A",
                "children": [
                    {
                        "id": 7,
                        "parent_id": 6,
                        "name": "财务部",
                        "children": []
                    },
                    {
                        "id": 108,
                        "parent_id": 6,
                        "name": "第二个部门",
                        "children": []
                    }
                ]
            }
        ];
        //调用此方法生成垂直菜单(class名和上面html元素的class名一致即可,可以任取)
        zl_ver_menu({
            data:dataList, //菜单数据
            menuClassName: "your-menu", //菜单容器元素的class名
            callback: function (par) { console.log(par) }, //点击具体的菜单项后要执行的函数(par默认会传入点击的节点)
            show: true //此垂直菜单默认为全部展开
        });

实现说明

1. 先准备一个 json 数据(对象数组)

let data = [
  {
    id: 6,
    parent_id: 0,
    name: "李冰有限公司A",
    children: [
      {
        id: 7,
        parent_id: 6,
        name: "财务部",
        children: [],
      },
      {
        id: 108,
        parent_id: 6,
        name: "第二个部门",
        children: [],
      },
    ],
  },
];

2. 循环递归这个对象数组,生成对应相关的菜单

/**
 * 作用与目的:通过递归创建垂直菜单
 * @item {Oject/Array} 要遍历递归的对象或者数组
 * @padding {Number} 每个垂直菜单的左padding值
 * @return {HTMLElement} 返回的html的菜单节点
 * */
export default function creatVerMenu(item, padding = 20) {
  //如果是对象数组,就调用自身,处理每个元素
  if (item instanceof Array) {
    let flag = true;
    item.forEach((ele) => {
      if (typeof ele !== "object") {
        flag = false; //说明不是对象数组
      }
    });
    if (flag) {
      //如果是对象数组,就遍历每一个对象
      let str = "<ul class='index-ver-nav'>";
      item.forEach((ele) => {
        str += creatVerMenu(ele, padding);
      });
      str = str + "</ul>";
      return str;
    }
  }

  //如果item是对象且具有孩子节点
  if (typeof item === "object" && item.children.length > 0) {
    let icon = "";
    if (item.icon) {
      icon = `<i class="${item.icon}"></i>`;
    } else {
      icon = `<i class="triangle"></i>`;
    }
    return `<li data-menu-id="${item.id}" data-parent-id="${
      item.parent_id
    }" ><div class="tit-menu" style="padding-left: ${padding}px" >${icon} <span>${
      item.name
    }</span></div>${creatVerMenu(item.children, padding + 20)}</li>`;
  }
  //没有孩子节点了
  if (typeof item === "object" && item.children.length === 0) {
    let icon = "";
    if (item.icon) {
      icon = `<i class="${item.icon}"></i>`;
    }
    return `<li sto_id="${item.id}" style="padding-left: ${padding}px"> ${icon} <span>${item.name}</span> </li>`;
  }
}

3. 给这个菜单注入样式,绑定事件

import creatVerMenu from "./creatVerMenu";
import data from "./testData";

/**
 * 作用与目的:调用creatVerMenu函数得到正在的菜单DOM
 * @parObj: 参数对象
 *  parObj.menuClassName {String} 构建菜单的元素class
 *  parObj.callback {function} 点击具体的菜单项后要执行的函数(par默认会传入点击的节点)
 *  parObj.show {boolean} 默认全部展开还是全部收缩,默认我true.全部展开
 * */
export default function getRealDom(
  parObj = {
    menuClassName: "zl-ver-menu",
    callback: function (par) {
      console.log(par);
    },
    show: true,
  }
) {
  // 先写入全局样式
  if (!$(`style.${parObj.menuClassName}-style`)[0]) {
    $("head").append(`
        <style class="${parObj.menuClassName}-style" >
        html,
        body {
            height: 100%;
            margin: 0;
            padding: 0;
        }

        /* 菜单收缩的三角形(默认) */
        .triangle {
            display: inline-block;
            border: 5px solid transparent;
            border-top: 5px solid #191F25;
        }

        /* 菜单展开的三角形 */
        .triangle_open {
            display: inline-block;
            border: 5px solid transparent;
            border-left: 5px solid #191F25;
        }

        /* 菜单选中时的颜色控制 */
        .summary-active {
            background-color: #E9F2FF !important;
            color: #4285f4 !important;
            border-right: 5px solid #4285F4;
        }

        .${parObj.menuClassName} {
            width: 230px;
            background: #fff;
            border: 1px solid #e7e7e9;
            border-top-width: 0px;
            border-bottom-width: 0px;
            overflow-y: auto;
            overflow-x: hidden;
            font-size: 16px;
            display: inline-block;
            height: 100%;
            vertical-align: top;
        }

        .${parObj.menuClassName} li {
            font-size: 12px;
            font-family: Microsoft YaHei;
            font-weight: 400;
            color: #70707a;
            padding-top: 10px;
            padding-bottom: 10px;
            cursor: pointer;
        }

        .${parObj.menuClassName} .tit-menu {
            padding-bottom: 10px;
            font-size: 14px;
            font-family: Microsoft YaHei;
            font-weight: 400;
            color: #191f25;
        }

        .${parObj.menuClassName} ul {
            padding-left: 0;
        }

        .${parObj.menuClassName} ul,
        .${parObj.menuClassName} li {
            list-style: none;
        }
    </style>
        `);
  }
  document.querySelector(`.${parObj.menuClassName}`).innerHTML = creatVerMenu(
    data
  );
  //设置垂直菜单的鼠标事件
  let i = 0;
  $(`.${parObj.menuClassName} li`).each(function () {
    if (!$(this).children("ul")[0]) {
      if (i == 0) {
        i++;
        setTimeout(() => {
          $(this).click();
          $(this).addClass("summary-active ");
        }, 0);
      }
      $(this).on({
        mouseover: function () {
          $(this).css({ "background-color": "#E9F2FF" });
        },
        mouseout: function () {
          $(this).css({ "background-color": "transparent" });
        },
      });
    }
  });
  // 给菜单添加点击事件,实现选中效果
  $(`.${parObj.menuClassName} li`).each(function () {
    if (!$(this).children("ul")[0]) {
      $(this).click(function () {
        $(`.${parObj.menuClassName} li`).removeClass("summary-active ");
        $(this).addClass("summary-active ");
        parObj.callback(this);
      });
    }
  });
  // 当点击了菜单标题时,展开和搜索图标
  $(`.${parObj.menuClassName} .tit-menu`).click(function () {
    $(this).parent().children("ul").slideToggle();
    let classv = $(this).find("i").attr("class");
    //这里因为字符串triangle_open包含了字符串triangle,所有顺序要注意下
    if (classv.includes("triangle_open")) {
      $(this).find("i").removeClass("triangle_open").addClass("triangle");
    } else {
      $(this).find("i").removeClass("triangle").addClass("triangle_open");
    }
  });
  if (!parObj.show) {
    $(`.${parObj.menuClassName}> ul >li > ul`).hide();
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值