js无限级分类数据列表

文章目录


前言

        页面中经常用到树菜单,大部分树菜单列表都是由后端处理,前端直接使用。或者前端生成固定层级的树菜单列表。我是因为在工作中被要求把一个二级菜单改造为支持三级菜单,且菜单列表仅是个list,不支持树,改造的过程中突发奇想万一后面再需要我继续改造四级,或者其他功能也需要这样显示,那我岂不是要重复很多工作任务,于是我决定封装自己无限树列表和树渲染。


一、普通列表改造为无限树列表

接口返回的菜单列表

var menulist = [
	{"id":1633,"name":"一级菜单1","pId":1632,"open":"false","type":"themecat"},
	    {"id":1635,"name":"二级菜单1.1","pId":1633,"open":"false","type":"themecat"},
	    {"id":1636,"name":"二级菜单1.2","pId":1633,"open":"false","type":"themecat"},
	    {"id":1634,"name":"二级菜单1.3","pId":1633,"open":"false","type":"themecat"},
			    {"id":22393,"name":"三级菜单1.3.1","pId":1634,"open":"false","type":"themecat"},
				{"id":22394,"name":"三级菜单1.3.2","pId":1634,"open":"false","type":"themecat"},
				{"id":22395,"name":"三级菜单1.3.3","pId":1634,"open":"false","type":"themecat"},
	    {"id":1637,"name":"二级菜单1.4","pId":1633,"open":"false","type":"themecat"},
	{"id":1638,"name":"一级菜单2","pId":1632,"open":"false","type":"themecat"},
	    {"id":1586,"name":"二级菜单2.1","pId":1638,"open":"false","type":"themecat"},
	    {"id":1585,"name":"二级菜单2.2","pId":1638,"open":"false","type":"themecat"},
	{"id":1641,"name":"一级菜单3","pId":1632,"open":"false","type":"themecat"},
	    {"id":1571,"name":"二级菜单3.1","pId":1641,"open":"false","type":"themecat"},
	    {"id":1572,"name":"二级菜单3.2","pId":1641,"open":"false","type":"themecat"},
	    {"id":4281,"name":"二级菜单3.3","pId":1641,"open":"false","type":"themecat"}
]

要求pid 对应id作为对应id的children,代码如下
 

function initTreeOption(treearr) {
    var obj = {}
      , menutree = [];
    // 这里遍历普通列表 把数组[]=>{}对象
    treearr.forEach((item)=>{
        // 这里可以加自己想要的属性,如class名之类的
        // 增加id前缀,避免实际id为数字时对象对键进行自动排序
        // 解构赋值避免生成menutree时对原来的数据有影响
        obj['id-' + item.id] = {...item}
    })
    for (var key in obj) {
        var menuObj = obj[key];
        if (menuObj) {
            // 取得当前节点的父节点
            var parent = obj['id-' + menuObj.pId];
            // 如果父节点存在则当前节点为叶节点
            if (parent) {
                // 在父节点的children下添加该叶节点
                parent.children = parent.children || [];
                parent.children.push(menuObj);
            } else {
                //否则为根节点
                menutree.push(menuObj);
            }
        }
    }
    //返回树列表
    return menutree;
}
var menutree = initTreeOption(menulist)

处理过后的数据,如下

[{
	"id": 1633,
	"name": "一级菜单1",
	"pId": 1632,
	"open": "false",
	"type": "themecat",
	"children": [{
		"id": 1635,
		"name": "二级菜单1.1",
		"pId": 1633,
		"open": "false",
		"type": "themecat"
	}, {
		"id": 1636,
		"name": "二级菜单1.2",
		"pId": 1633,
		"open": "false",
		"type": "themecat"
	}, {
		"id": 1634,
		"name": "二级菜单1.3",
		"pId": 1633,
		"open": "false",
		"type": "themecat",
		"children": [{
			"id": 22393,
			"name": "三级菜单1.3.1",
			"pId": 1634,
			"open": "false",
			"type": "themecat"
		}, {
			"id": 22394,
			"name": "三级菜单1.3.2",
			"pId": 1634,
			"open": "false",
			"type": "themecat"
		}, {
			"id": 22395,
			"name": "三级菜单1.3.3",
			"pId": 1634,
			"open": "false",
			"type": "themecat"
		}]
	}, {
		"id": 1637,
		"name": "二级菜单1.4",
		"pId": 1633,
		"open": "false",
		"type": "themecat"
	}]
}, {
	"id": 1638,
	"name": "一级菜单2",
	"pId": 1632,
	"open": "false",
	"type": "themecat",
	"children": [{
		"id": 1586,
		"name": "二级菜单2.1",
		"pId": 1638,
		"open": "false",
		"type": "themecat"
	}, {
		"id": 1585,
		"name": "二级菜单2.2",
		"pId": 1638,
		"open": "false",
		"type": "themecat"
	}]
}, {
	"id": 1641,
	"name": "一级菜单3",
	"pId": 1632,
	"open": "false",
	"type": "themecat",
	"children": [{
		"id": 1571,
		"name": "二级菜单3.1",
		"pId": 1641,
		"open": "false",
		"type": "themecat"
	}, {
		"id": 1572,
		"name": "二级菜单3.2",
		"pId": 1641,
		"open": "false",
		"type": "themecat"
	}, {
		"id": 4281,
		"name": "二级菜单3.3",
		"pId": 1641,
		"open": "false",
		"type": "themecat"
	}]
}]

二、无限树菜单dom渲染

1.使用递归进行dom渲染代码如下:

function renderTree(menutree, $elem) {
    // 内部初始化html文本方法
    function initTreeHml(menutree) {
        // 如果menutree不存在或长度为0 返回空
        if(!menutree || menutree.length == 0){
            return ''
        }
        var treeHtml = ['<ul class="tree-ul">'];
        for (var i = 0; i < menutree.length; i++) {
            var item = menutree[i];
            // 渲染父节点html
            if (item.children && item.children.length) {
                //生成本级html
                treeHtml.push('<li class="tree-li tree-parent"><a class="tree-li-title" href="javascript:void(0)" data-haschild="true"  data-id=" ' + item.id + '" data-mc=" ' + item.name + '"><label class="tree-li-title-label">' + item.name + '</label><i class="tree-li-icon inactive"></i></a>');
                // 递归调用自身 生成叶节点的html
                treeHtml.push(initTreeHml(item.children) + '</li>');
            } else {
                // 渲染叶节点html
                treeHtml.push('<li class="tree-li tree-leaf"><a class="tree-li-title" href="javascript:void(0)" id="' + item.id + '" mc="' + item.name + '"><label class="tree-li-title-label">' + item.name + '</label></a></li>');
            }
        }
        treeHtml.push('</ul>')
        //html拼接
        return treeHtml.join('')
    }
    // dom渲染
    $elem.html(initTreeHml(menutree));
}

2.全部代码

<html>
	<head>
		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<style>
			.tree-con {
				width: 270px;
				border: 1px solid #efefef;
			}

			.tree-ul {
				line-height: 2em;
				padding: 0;
			}

			.tree-li {
				list-style: none;
			}

			.tree-parent {
				padding: 0 10px;
			}

			.tree-parent .tree-ul {
				display: none;
				padding-left: 20px;
			}

			.tree-li-title {
				display: block;
				height: 30px;
				padding: 0 10px;
				text-decoration: none;
				color: #5a5757;
			}

			.tree-parent>.tree-li-title {
				margin: 1px 0;
				background: #f7dede;
				border-bottom: 1px solid #bdbdbd;
			}

			label.tree-li-title-label {
				display: inline-block;
				width: 100%;
				vertical-align: middle;
			}

			.tree-li-icon {
				float: right;
				margin-top: -2px;
				font-size: 24px;
				font-style: normal;
				-webkit-font-smoothing: antialiased;
				-moz-osx-font-smoothing: grayscale;
				margin-left: -30px;
			}

			.add:before {
				content: '+';
			}

			.minus:before {
				content: '-';
			}
		</style>
	</head>
	<body>
		<div id="tree-menu" class="tree-con"></div>
		<script type="text/javascript" src="https://xinjiang.chinatax.gov.cn/images/jquery-1.11.3.js"></script>
		<script>
			$(function(){
			var menulist = [
				{"id":1633,"name":"一级菜单1","pId":1632,"open":"false","type":"themecat"},
				    {"id":1635,"name":"二级菜单1.1","pId":1633,"open":"false","type":"themecat"},
				    {"id":1636,"name":"二级菜单1.2","pId":1633,"open":"false","type":"themecat"},
				    {"id":1634,"name":"二级菜单1.3","pId":1633,"open":"false","type":"themecat"},
						    {"id":22393,"name":"三级菜单1.3.1","pId":1634,"open":"false","type":"themecat"},
							{"id":22394,"name":"三级菜单1.3.2","pId":1634,"open":"false","type":"themecat"},
							{"id":22395,"name":"三级菜单1.3.3","pId":1634,"open":"false","type":"themecat"},
				    {"id":1637,"name":"二级菜单1.4","pId":1633,"open":"false","type":"themecat"},
				{"id":1638,"name":"一级菜单2","pId":1632,"open":"false","type":"themecat"},
				    {"id":1586,"name":"二级菜单2.1","pId":1638,"open":"false","type":"themecat"},
				    {"id":1585,"name":"二级菜单2.2","pId":1638,"open":"false","type":"themecat"},
				{"id":1641,"name":"一级菜单3","pId":1632,"open":"false","type":"themecat"},
				    {"id":1571,"name":"二级菜单3.1","pId":1641,"open":"false","type":"themecat"},
				    {"id":1572,"name":"二级菜单3.2","pId":1641,"open":"false","type":"themecat"},
				    {"id":4281,"name":"二级菜单3.3","pId":1641,"open":"false","type":"themecat"}
			]
				// 初始化树菜单列表
				var menutree = initTreeOption(menulist);
				// 渲染树菜单
				renderTree(menutree,$('#tree-menu'));
				// 监听点击事件
				$('#tree-menu').on('click','.tree-li-title',event);
			})
			
			function initTreeOption(treearr) {
			    var obj = {}
			      , menutree = [];
			    // 这里遍历普通列表 把数组[]=>{}对象
			    treearr.forEach((item)=>{
			        // 这里可以加自己想要的属性,如class名之类的
			        // 增加id前缀,避免实际id为数字时对象对键进行自动排序
			        // 解构赋值避免生成menutree时对原来的数据有影响
			        obj['id-' + item.id] = {...item}
			    })
			    for (var key in obj) {
			        var menuObj = obj[key];
			        if (menuObj) {
			            // 取得当前节点的父节点
			            var parent = obj['id-' + menuObj.pId];
			            // 如果父节点存在则当前节点为叶节点
			            if (parent) {
			                // 在父节点的children下添加该叶节点
			                parent.children = parent.children || [];
			                parent.children.push(menuObj);
			            } else {
			                //否则为根节点
			                menutree.push(menuObj);
			            }
			        }
			    }
			    //返回树列表
			    return menutree;
			}
			
			function renderTree(menutree,$elem) {
			    // 内部初始化html文本方法
			    function initTreeHml(menutree) {
			        // 如果menutree不存在或长度为0 返回空
			        if(!menutree || menutree.length == 0){
			            return ''
			        }
			        var treeHtml = ['<ul class="tree-ul">'];
			        for (var i = 0; i < menutree.length; i++) {
			            var item = menutree[i];
			            // 渲染父节点html
			            if (item.children && item.children.length) {
			                //生成本级html
			                treeHtml.push('<li class="tree-li tree-parent"><a class="tree-li-title" href="javascript:void(0)" data-haschild="true"  data-id=" ' + item.id + '" data-mc=" ' + item.name + '"><label class="tree-li-title-label">' + item.name + '</label><i class="tree-li-icon add"></i></a>');
			                // 递归调用自身 生成叶节点的html
			                treeHtml.push(initTreeHml(item.children) + '</li>');
			            } else {
			                // 渲染叶节点html
			                treeHtml.push('<li class="tree-li tree-leaf"><a class="tree-li-title" href="javascript:void(0)" id="' + item.id + '" mc="' + item.name + '"><label class="tree-li-title-label">' + item.name + '</label></a></li>');
			            }
			        }
			        treeHtml.push('</ul>')
			        //html拼接
			        return treeHtml.join('')
			    }
			    // dom渲染
			    $elem.html(initTreeHml(menutree));
			}
			
			function event(e){
				var $this = $(this),
				    $next = $this.next(),
					$parent = $this.parent();
				$this.toggleClass('active');
			    if($parent.is('.tree-parent')){
			        if($this.is('.active')){
			            $this.children('.tree-li-icon').removeClass('add').addClass('minus');
			            $next.slideDown();
			        }else{
			        	$next.children('ul').css('display','none');
			        	$parent.find('.minus').removeClass('minus').addClass('add');
						$next.slideUp();
			        }
			    }else{
					//叶节点事件
			    }
			}
		</script>
	</body>
</html>


三、效果图

总结

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值