layui原生layui-nav-tree仅支持两级菜单,三级及以上需用dropdown.render实现无限层级或手写dom+css兼容老项目;数据须嵌套结构,路径匹配需清理斜杠并冒泡激活态。

Layui 原生的 layui-nav-tree 仅支持两级菜单(一级 <li> + 二级 <dl class="layui-nav-child"></dl>),三级及更深层级无法被识别和渲染——即使 HTML 中手动嵌套了 <dl><dd><dl></dl></dd></dl>,CSS 和 JS 逻辑也不会向下递归处理,结果是第三级菜单不显示、错位或被忽略。
用 dropdown.render 实现真正可用的三级/四级垂直菜单
这是 Layui 官方唯一明确支持无限层级嵌套的方案,无需改源码,语义清晰,交互可控。
- 菜单数据必须使用嵌套结构,每层通过
child字段定义子项:{ title: "系统管理", child: [{ title: "用户中心", child: [{ title: "新增用户", href: "/user/add" }, { title: "用户列表", href: "/user/list" }] }] } - 触发元素推荐用
<span></span>或<a></a>,避免<button></button>带来默认样式干扰布局 - 关键配置项:
align: 'left'让下拉面板紧贴左侧垂直展开;offset: '0,4'微调垂直间距,防止与父项重叠;trigger: 'click'(非 hover)更适合侧边栏操作习惯 - 点击回调中需判断是否为叶子节点:
若data.child === undefined才执行跳转;否则只展开子菜单
手写 DOM + CSS 实现兼容老项目的三级/四级折叠
适用于已深度绑定 layui-nav-tree 结构、不便引入 dropdown 的场景。核心是绕过 Layui 渲染逻辑,完全由 JS 控制显隐。
- HTML 中保留原结构,但所有三级及以后菜单统一加自定义类名,例如:
<ul class="sub-menu-level3"><li>…</li></ul>,并设style="display:none" - 在二级菜单项末尾插入展开图标:
<i class="layui-icon layui-icon-down" data-level="2"></i> - JS 绑定点击事件:
点击二级项时,调用$(this).next('.sub-menu-level3').slideToggle();
同时切换图标为layui-icon-down/layui-icon-up - 必须加
e.stopPropagation(),否则会触发layui-nav自带的折叠行为,导致菜单闪退 - 注意:Layui 的
element.init()会劫持所有.layui-nav-item点击,自定义事件应在element.init()之后再绑定
动态渲染时的数据与路径匹配要点
后台返回的菜单 JSON 必须是严格嵌套结构,不能是扁平的 id/pid 列表;前端直接传入会导致丢层。
- ✅ 正确格式示例:
[{ "name": "权限管理", "href": "/auth", "children": [{ "name": "角色管理", "href": "/auth/role", "children": [{ "name": "分配权限", "href": "/auth/role/assign" }] }] }] - 刷新后高亮失效?别直接比对
window.location.pathname和菜单href。
统一做清理:const path = location.pathname.replace(/^\/+|\/+$/g, ''),再逐个===匹配 - 激活态要向上冒泡:找到匹配的四级菜单项后,不仅给它加
layui-this,还要依次给它的三级父<dd></dd>、二级父<dl></dl>、一级父<li>添加对应状态类(如layui-nav-itemed) - 异步加载菜单时,路径匹配逻辑必须放在
navbar.done回调里,确保 DOM 已渲染完成
不复杂但容易忽略。关键是选对方案:新项目优先用 dropdown.render;存量项目若结构固化,就用手工控制 DOM 显隐。数据结构和路径匹配这两关过了,多级菜单就稳了。











