原生js递归树组件

效果图:
在这里插入图片描述

新建一个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>


  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值