一、需求分析
① 从 JSON 文件加载层级化菜单数据
②支持菜单折叠 / 展开,点击父项切换状态
③箭头图标随菜单状态旋转,直观反馈交互结果
④样式美观,层级结构清晰
二、实现步骤详解
1. 基础结构与样式搭建
首先创建 HTML 容器,定义菜单的基础样式,包括容器布局、菜单项间距、箭头图标样式及过渡效果。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>树形菜单</title>
<style>
.box {
width: 255px;
background-color: #cfd9eb;
border-radius: 10px;
padding: 10px 0; /* 增加内边距,优化布局 */
}
.sub-menu {
padding-left: 30px;
margin: 0; /* 清除默认边距 */
list-style: none; /* 去掉默认列表样式 */
}
.sub-item {
line-height: 40px;
cursor: pointer;
width: 100%;
font-size: 20px;
padding: 0 10px; /* 增加左右内边距 */
}
.sub-item:hover {
background-color: #b8c6db; /* hover效果,提升交互感 */
}
.sub-item img {
width: 25px;
height: 25px;
vertical-align: middle;
margin-left: 7px;
transition: transform 0.3s ease; /* 箭头旋转过渡 */
}
.rotate {
transform: rotate(180deg); /* 箭头旋转180度 */
}
</style>
</head>
<body>
<div class="box"></div>
<!-- 引入jQuery -->
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
</body>
</html>
2. JSON 数据格式设计
创建json文件,用id标识节点唯一值,pid表示父节点 ID(根节点 pid 为 "0"),name为菜单名称,通过这三个字段实现层级关联。
[
{"id": "1", "pid": "0", "name": "菜单1"},
{"id": "1-1", "pid": "1", "name": "菜单1-1"},
{"id": "1-2", "pid": "1", "name": "菜单1-2"},
{"id": "1-2-1", "pid": "1-2", "name": "菜单1-2-1"},
{"id": "2", "pid": "0", "name": "菜单2"},
{"id": "2-1", "pid": "2", "name": "菜单2-1"},
{"id": "3", "pid": "0", "name": "菜单3"}
]
3. AJAX 请求加载数据
使用 jQuery 的$.ajax()方法请求 JSON 文件,成功后获取数据并调用渲染函数,同时默认隐藏所有子菜单(初始只显示根节点)。
$(document).ready(function () {
var menuData = [];
// 发送AJAX请求获取菜单数据
$.ajax({
url: './js/07-树形菜单.json',
type: 'GET',
dataType: 'json',
success: function (data) {
menuData = data;
renderMenu(data); // 调用渲染函数
$(".box ul ul").hide(); // 初始隐藏所有子菜单
},
error: function (err) {
console.error('菜单数据加载失败:', err);
}
});
});
4. 递归渲染层级菜单
树形菜单的核心是层级渲染,通过递归函数renderMenu,根据pid筛选当前节点的子节点,动态创建ul和li元素,嵌套生成多级菜单。
// 渲染菜单函数:data-数据源,pid-父节点ID,$parent-父容器
function renderMenu(data, pid = "0", $parent = $(".box")) {
// 筛选当前父节点的所有子节点
var children = data.filter(item => item.pid === pid);
if (children.length === 0) return; // 无子女节点则终止递归
// 创建子菜单容器
var $ul = $("<ul class='sub-menu'></ul>");
$parent.append($ul);
// 遍历子节点,创建菜单项
children.forEach(item => {
// 菜单项包含名称和箭头图标
var $li = $("<li class='sub-item'></li>").html(`${item.name} <img src="./img/向下箭头.png" alt="切换">`);
$ul.append($li);
// 递归渲染当前节点的子菜单
renderMenu(data, item.id, $li);
});
}
5. 实现折叠 / 展开交互
为菜单项绑定点击事件,通过toggle()切换子菜单显示状态,用toggleClass("rotate")实现箭头旋转,同时通过e.stopPropagation()阻止事件冒泡(避免父节点联动触发)。
// 菜单点击事件:委托绑定(适配动态生成的节点)
$(".box").on("click", ".sub-item", function (e) {
e.stopPropagation(); // 阻止事件冒泡
var $childUl = $(this).children(".sub-menu"); // 获取当前节点的子菜单
if ($childUl.length > 0) { // 只有存在子菜单时才触发切换
$childUl.toggle(); // 切换子菜单显示/隐藏
$(this).find("img").toggleClass("rotate"); // 箭头旋转切换
}
});
三、关键技术点解析
1. 递归渲染的核心逻辑
递归的本质是 “自身调用自身”,这里通过pid关联父节点和子节点:
- 每次调用筛选出当前
pid对应的子节点 - 为每个子节点创建菜单项后,再以该节点的
id为pid,递归渲染下一级菜单 - 无子女节点时终止递归,避免死循环
2. 事件委托的应用
由于菜单节点是动态生成的,直接绑定click事件会失效,因此使用$(parent).on("click", "child", function())的委托绑定方式,让父容器监听子节点的点击事件。
感 谢 大 家 的 观 看 !! !!
如有补充,欢迎各位大神评论区留言~
928

被折叠的 条评论
为什么被折叠?



