理论上菜单层级可以无限多,因为是递归渲染。
gif效果图:
代码
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>树形菜单title>
head>
<style>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-size: 14px;
}
body{
background-color: #444;
}
/*侧栏菜单 */
.aside_menu {
position: fixed;
min-width: 350px;
height: 700px;
background-color: #333;
padding: 20px;
border: 0;
border-right: 3px solid #333;
cursor: pointer;
color: white;
overflow: auto;
}
/* 菜单项 */
.menuItem{
margin-bottom: 5px;
height: 30px;
line-height: 30px;
padding-right: 10px;
}
.menuItem:hover {
color: #333;
background-color: #bbcaca;
}
/* 有子菜单的菜单项 */
[data-child-count]::after {
content: '<';
opacity: 0.3;
float: right;
}
/* 有子菜单的菜单项 */
.expandable::after {
content: '>';
opacity: 0.3;
float: right;
}
/* 子菜单折叠 */
.collapsable {
display: none;
}
style>
<body>
<div class="aside_menu">
<div class="menuwrraper">div>
div>
<script>
/**
* 菜单对象说明:
{
id: '0',//菜单id
name: '菜单名',//菜单名称
submenu: []//子菜单集合
},
**/
let menuRoot = {
id: '0',
name: '树形菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: []
},
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: []
},
]
},
]
},
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: []
},
]
},
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: []
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: []
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: [
{
id: '0',
name: '菜单',
submenu: []
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
},
]
}
/**
* menuWrraper-菜单最外层包装盒子,用于包裹当前菜单项及当前菜单项的子菜单
* level-菜单层级,默认为L0,依次为L1,L2,...
* menuData-菜单对象
* paddingLeft-菜单左内边距,会根据菜单层级Level依次增加paddingIncrement
* paddingIncrement-默认为20,单位为 px
* */
function renderMenu(menuData, menuWrraper, level = 0, paddingLeft = 0, paddingIncrement = 20) {
if (menuData != null) {
// 0.创建当前菜单
const menuItem = document.createElement('div')
// 0.1 添加菜单id
menuItem.dataset.id = menuData.id
// 0.2 添加菜单层级L0,L1,L2,...
menuItem.classList.add(`L${level}`)
// 0.3 用于控制鼠标 hover效果
menuItem.classList.add('menuItem')
// 0.4 菜单左内边距
menuItem.style.paddingLeft = `${paddingLeft}px`
// 0.5 菜单内容
menuItem.innerHTML = `${menuData.name}`
// 0.6 ****追加当前菜单盒子
menuWrraper.appendChild(menuItem)
const submenu = menuData.submenu
if (submenu.length > 0) {// 如果当前菜单有子菜单,则
// 1. 为当前菜单追加一个属性,表示其有 子菜单
// menuItem.dataset.hasChild = 1
menuItem.dataset.childCount = submenu.length
// 2. 为当前菜单注册点击事件,可折叠展开子菜单、切换折叠展开图标
menuItem.addEventListener('click', () => {
// 2.1 ****用于CSS渲染可展开的最右侧小图标:‘<’ 或者 ‘>’
menuItem.classList.toggle('expandable')
// 2.2 ****用于控制子菜单折叠与展开,也即控制submenuWrraper的dispaly属性为‘none’或者‘block’
menuItem.nextElementSibling.classList.toggle('collapsable')
})
// 3. 菜单层级加1
level++;
// 4. 左边距增加
paddingLeft += paddingIncrement
// 5. 创建子菜单包装盒子
const submenuWrraper = document.createElement('div')
// 7.*****追加子菜单盒子,追加这个盒子的原因是 2.2
menuWrraper.appendChild(submenuWrraper)
// 6.*****循环当前菜单的子菜单
for (let i = 0; i < submenu.length; i++) {
// 6.1 ****递归渲染每一项子菜单
renderMenu(submenu[i], submenuWrraper, level, paddingLeft)
}
}
}
}
// 调用菜单渲染函数生成页面数据
renderMenu(menuRoot, document.querySelector('.menuwrraper'))
// 渲染完毕后,释放菜单数据
menuRoot = null
script>
body>
html>