菜单层级理论上可以无限多,因为是递归渲染。
gif演示图:
代码:
树形菜单.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>
* {
padding: 0;
margin: 0;
box-sizing: border-box;
font-size: 14px;
color: #fff;
}
body {
background-color: #222;
}
.aside-menu {
position: fixed;
padding: 20px;
width: 300px;
height: 700px;
background-color: #333;
overflow: auto;
}
ul {
list-style: none;
}
.aside-menu a {
display: block;
text-decoration: none;
height: 30px;
line-height: 30px;
}
.active {
color: #00ff30;
}
.aside-menu a:hover {
background-color: #727171;
}
[data-childs]::after {
content: '-';
float: right;
}
.expand::after {
content: '+';
float: right;
}
.notdisplay {
display: none;
}
</style>
</head>
<body>
<div class="aside-menu">
<ul class="menuwrapper">
</ul>
</div>
<script>
// 菜单数据集合
let menuArray = [
{
id: '0',//菜单id
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: []
},
]
/**
* menuData-菜单数组
* menuwrapper-当前菜单项包裹盒子
* level-菜单层级,L0,L1,L2,...
* paddingLeft-当前菜单的子菜单左内边距,制造树形缩进效果
* paddingLeftIncrement-每层菜单左内边距增加值,默认20(px)
*/
function renderMenu(menuData, menuwrapper, level = 0, paddingLeft = 0, paddingLeftIncrement = 20) {
if (menuData && menuData.length > 0) {
let l = level
menuwrapper.classList.add(`L${l}`)
menuwrapper.style.paddingLeft = `${paddingLeft}px`
for (let i = 0; i < menuData.length; i++) {
/**
* <ul class="menuwrapper">
* <li class="menuitem">
* <a href="#" data-childs="3" class="expand"><span class="active">菜单名</span>::after</a>
* <ul class="notdisplay">
* </ul>
* </li>
* </ul>
*/
const menuItem = document.createElement('li')
menuItem.classList.add('menuitem')
const link = document.createElement('a')
link.href = '#'
link.innerHTML = `<span>${menuData[i].name}</span>`
menuItem.appendChild(link)
menuwrapper.appendChild(menuItem)
const submenu = menuData[i].submenu
if (submenu.length > 0) {
l++
link.dataset.childs = submenu.length
const submenuWrapper = document.createElement('ul')
// 展开折叠图标切换、子菜单显示隐藏切换
link.addEventListener('click', () => {
link.classList.toggle('expand')
link.nextElementSibling.classList.toggle('notdisplay')
})
paddingLeft = paddingLeftIncrement
// 递归渲染子菜单数组
renderMenu(submenu, submenuWrapper, l, paddingLeft)
menuItem.appendChild(submenuWrapper)
}
}
}
}
const menuwrapper = document.querySelector('.menuwrapper')
// 活跃菜单
menuwrapper.addEventListener('click', (e) => {
if (e.target.tagName === 'A') {
let activeLink = document.querySelector('.active')
if (activeLink) activeLink.classList.remove('active')
e.target.firstElementChild.classList.add('active')
}
})
// 渲染
renderMenu(menuArray, menuwrapper)
// 释放菜单数据
menuArray = null
</script>
</body>
</html>