菜单层级理论上可以无限多,因为是递归渲染。
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',//菜单idname: '树形菜单',//菜单名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 = levelmenuwrapper.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].submenuif (submenu.length > 0) {l++link.dataset.childs = submenu.lengthconst 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>