效果图:
新建一个html文件,将代码粘贴进去,打开浏览器就能看到效果
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
<meta http-equiv="Content-Security-Policy">
<title>js实现无限级树形导航列表</title>
<style type="text/css">
*{
margin:0;
padding:0;
list-style:none;
}
body {
margin:20px;
}
#nav a {
text-decoration:underline;
color:#06c;
font-size:14px;
line-height:24px;
}
#nav ul{
margin-bottom:5px;
}
#nav strong{
color:#696;
}
#nav.dyn li ul{
display:none;
}
#nav.dyn li ul.show{
display:block;
}
#nav.dyn li{
padding-left:15px;
position: relative;
}
/* 下面两行样式是用伪类写的折叠展开样式 */
#nav.dyn li.parent:after{
content: '';
border-width:5px;
border-style:solid;
border-color:transparent transparent transparent black;
position: absolute;
top: 7px;
left: 0;
}
#nav.dyn li.open:after{
content:"";
border-width:5px;
border-style:solid;
border-color:black transparent transparent transparent;
position: absolute;
top: 7px;
left: 0;
}
</style>
</head>
<style>
</style>
<body>
<ul id="nav"></ul>
</body>
<script>
window.sn = {
dynamicClass: 'dyn',
showClass: 'show',
parentClass: 'parent',
openClass: 'open',
navID: 'nav',
init: function () {
var triggerLink;
if (!document.getElementById || !document.createTextNode) { return; }
var nav = document.getElementById(sn.navID);
if (!nav) { return; }
sn.cssjs('add', nav, sn.dynamicClass);
var nested = nav.getElementsByTagName('a');
for (var i = 0; i < nested.length; i++) {
triggerLink = nested[i];
// 添加点击事件
sn.addEvent(triggerLink, 'click', sn.changeSection, false);
triggerLink.onclick = false;
// 添加展开折叠小箭头样式
var firstList = triggerLink.parentNode.getElementsByTagName('ul')[0];
if (firstList) {
sn.cssjs('add', triggerLink.parentNode, sn.parentClass);
}
}
},
// 点击tree事件
changeSection: function (e) {
console.log(e.target.getAttribute('data-trageId'),e)
var t = sn.getTarget(e);
var firstList = t.parentNode.getElementsByTagName('ul')[0];
if (!firstList) {return}
if (sn.cssjs('check', firstList, sn.showClass)) {
sn.cssjs('remove', firstList, sn.showClass)
sn.cssjs('swap', t.parentNode, sn.openClass, sn.parentClass);
} else {
sn.cssjs('add', firstList, sn.showClass)
sn.cssjs('swap', t.parentNode, sn.openClass, sn.parentClass);
}
sn.cancelClick(e);
},
cancelClick:function(e){
if (window.event){
window.event.cancelBubble = true;
window.event.returnValue = false;
}
if (e && e.stopPropagation && e.preventDefault){
e.stopPropagation();
e.preventDefault();
}
},
addEvent: function(elm, evType, fn, useCapture){
if (elm.addEventListener){
elm.addEventListener(evType, fn, useCapture);
return true;
} else if (elm.attachEvent) {
var r = elm.attachEvent('on' + evType, fn);
return r;
} else {
elm['on' + evType] = fn;
}
},
cssjs:function(a,o,c1,c2){
switch (a){
case 'swap':
o.className=!sn.cssjs('check',o,c1)?o.className.replace(c2,c1):o.className.replace(c1,c2);
break;
case 'add':
if(!sn.cssjs('check',o,c1)){
o.className += o.className?' '+c1:c1;
}
break;
case 'remove':
var rep=o.className.match(' '+c1)?' '+c1:c1;
o.className=o.className.replace(rep,'');
break;
case 'check':
var found=false;
var temparray=o.className.split(' ');
for(var i=0;i<temparray.length;i++){
if(temparray[i]==c1){
found=true;
}
}
return found;
break;
}
},
getTarget:function(e){
var target = window.event ? window.event.srcElement : e ? e.target : null;
if (!target){return false;}
while(target.nodeType!=1 && target.nodeName.toLowerCase()!='body'){
target=target.parentNode;
}
return target;
},
}
// tree数据
var treeData = [
{
id: '1',
equName: '总目录',
children: [
{
id: '1.1',
equName: '1.1级目录',
children: [
{
id: '1.1.1',
equName: '1.1.1级目录',
children: [
{
id: '1.1.1.1',
equName: '1.1.1.1级目录',
children: [
{
id: '1.1.1.1.1',
equName: '1.1.1.1.1级目录',
children: [
{
id: '1.1.1.1.1.1',
equName: '1.1.1.1.1.1级目录',
}
]
}
],
},
{
id: '1.1.1.2',
equName: '1.1.1.2级目录',
}
]
},
{
id: '1.1.2',
equName: '1.1.2级目录',
},
]
},
{
id: '1.2',
equName: '1.2级目录',
children: [
{
id: '1.2.1',
equName: '1.2.1级目录',
children: []
}
],
}
]
}
]
// 动态渲染tree树 (data-trageId 是往标签中写入一个id值,再点击标签时可以过去到)
var loop = (treeData) => {
return treeData.map(item => {
return (item.children && item.children.length > 0) ?
`<li>
<a href="#" data-trageId="${item.id}">${item.equName}</a>
<ul>
${loop(item.children)}
</ul>
</li>
`
: `<li><a href="#" data-trageId="${item.id}">${item.equName}</a></li>`
})
}
// 这里有个bug,递归循环出来的loop中有一个逗号,时间紧没有排查,用replaceAll去掉了,做法有些槽,后面再补充
var html = loop(treeData).join('').replaceAll(",","")
// nav:因为id是全局唯一的,所以这里可以不用document.getElementById() 方法来获取
nav.innerHTML = html
window.sn.init()
</script>
</html>