JS实现简单树形结构菜单

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

思路:

  • 在无限极别的菜单中,数据一般不是固定的,需要从服务器请求获取:
  • 如果数据量不大,一般是一次请求数据,获取到所有级别的信息
  • 如果数据量比较大,一般只是请求当前这一级的信息,点击展示的时候,再去请求下一级信息(再去动态生成下一级的DOM内容)
  • 创建DOM的时候,有的是一次性把所有级别的DOM创建好(数据量不大),控制显示隐藏,有的也只是渲染第一级别的DOM,点击哪个再去渲染哪个级别的DOM
  • 再优化,可能还要对已经获取的数据或者已经渲染的DOM做缓存。

添加事件:

给当前树形折叠菜单中的所有按钮绑定点击事件【事件委托】=> 点击 " + "/ " - " 控制当前级别的显示隐藏

  • 分别给每个元素的事件行为绑定方法(循环绑定的时候,如果后续需要用到索引,必然会产生闭包;并且创建了大量的方法;而且必须保证当前元素已经存在了;…)
  • 事件委托是依赖于事件的冒泡传播机制,我们只需要给最外层元素的当前事件行为绑定方法,这样不论触发容器中哪一个元素的相关事件行为,都会传播到最外层的元素上,最外层绑定的方法触发,在方法中,我们基于当前触发的事件源不同,做不同的事情…
  • 没有循环,不存在创建大量闭包的操作;只创建了一个方法,不用开辟大量的堆内存;也不需要单独获取每个元素,所以事件绑定的时候元素是否存在无所谓,只要能触发这个元素,说明这个元素肯定存在了(实现了给动态绑定的元素做事件绑定的效果);还有很多操作,不用事件委托实现不了;事件委托/事件代理性能可以提高40%~60%…
<!-- import css -->
<link rel="stylesheet" href="css/reset.min.css">
<style>
.container {box-sizing: border-box;margin: 20px auto;padding: 10px;
	width: 600px;border: 1px dashed #AAA;-webkit-user-select: none;}
.level {display: none;font-size: 14px;margin-left: 10px;}
.level.level0 {display: block;margin-left: 0;}
.level li {position: relative;padding-left: 15px;line-height: 30px;}
.level li .icon {position: absolute;left: 0;top: 9px;
	box-sizing: border-box;width: 12px;height: 12px;
	line-height: 8px;text-align: center;border: 1px solid #AAA;
	background: #EEE;cursor: pointer;}
.level li .icon:after {display: block;content: "+";font-size: 12px;font-style: normal;}
.level li .icon.open:after {content: "-";}
.level li .title {color: #000;}
</style>

<div class="container">
	<ul class="level level0">	
	</ul>
</div>

<script src="js/jquery.min.js"></script>
<script>
 //加载数据
const queryData = function queryData() {
  return new Promise(function(resolve, rejected) {
      $.ajax({
          url: "./data.json",
          method: "get",
          dataType: "json",
          success: function(result) {
              resolve(result);
          }
      })
  })
};
 // 数据绑定
const bindHTML = function bindHTML(result, n) {
    let str = ``;
    n++;
   result.forEach((item, index) => {
   		let {
            name,
             open,
             children
         } = item;
       	str += `<li>
		   <a href="#" class="title">${name}</a>
		   ${children && children.length>0?`
		       <em class="icon ${open?'open':''}"></em>
		       <ul class="level level${n}" 
		           style="display:${open?'block':'none'}">
		           ${bindHTML(children)}
		       </ul>
		   `:''}
    	</li>`;
	});
	n--;
	return str;
};
$(async function() {
	let $level = $(".level");
  	// 从服务器获取数据
  	let result = await queryData();
  	// 数据绑定
	let n = 0;
	$level.html(bindHTML(result,n));
  	//点击 " + "/ " - " 控制当前级别的显示隐藏
  	$level.click(function (ev) {
		// 获取事件源:点击的是谁,事件源就是谁
		let target = ev.target,
		$target = $(target);
		// 点击的是EM
		if (target.tagName === 'EM') {
			let $ul = $target.next('ul');
			$ul.stop().slideToggle(200);
			$target.toggleClass('open');
		};
	});
});
</script>

data.json

[{
	"name": "前端开发基础",
	"open": true,
	"children": [{
			"name": "HTML5核心知识",
			"children": [{
					"name": "新增语义化标签"
				},
				{
					"name": "表单元素新特性"
				},
				{
					"name": "音视屏处理"
				},
				{
					"name": "canvas和webGL"
				},
				{
					"name": "新增JS中的API"
				}
			]
		},
		{
			"name": "CSS3核心知识",
			"children": [{
					"name": "新增选择器"
				},
				{
					"name": "字体图标"
				},
				{
					"name": "常用的样式属性"
				},
				{
					"name": "背景的处理"
				},
				{
					"name": "transform变形"
				},
				{
					"name": "CSS3动画",
					"children": [{
							"name": "transition过度动画"
						},
						{
							"name": "animation帧动画"
						},
						{
							"name": "3D动画的处理"
						}
					]
				},
				{
					"name": "新盒子模型属性",
					"children": [{
							"name": "flex弹性盒子模型"
						},
						{
							"name": "box-sizing新盒子模型属性"
						},
						{
							"name": "cloumns多列布局"
						}
					]
				}
			]
		},
		{
			"name": "实战案例和布局技巧",
			"children": [{
					"name": "实战案例练习",
					"children": [{
							"name": "居中处理"
						},
						{
							"name": "同行排列"
						},
						{
							"name": "圣杯布局"
						},
						{
							"name": "双飞翼布局"
						},
						{
							"name": "滑动门"
						},
						{
							"name": "面包屑导航"
						}
					]
				},
				{
					"name": "响应式布局开发",
					"children": [{
							"name": "viewport和dpi适配"
						},
						{
							"name": "@media媒体查询"
						},
						{
							"name": "rem等比缩放"
						},
						{
							"name": "百分比布局"
						}
					]
				}
			]
		}
	]
}, {
	"name": "前端开发核心",
	"children": [{
			"name": "JS(ES6)核心",
			"children": [{
					"name": "基础知识"
				},
				{
					"name": "闭包作用域及堆栈内存"
				},
				{
					"name": "面向对象和THIS处理"
				},
				{
					"name": "同步异步(事件循环、微任务、宏任务)"
				},
				{
					"name": "DOM事件和事件委托"
				},
				{
					"name": "设计模式"
				}
			]
		},
		{
			"name": "AJAX前后端交互",
			"children": [{
					"name": "AJAX基础知识"
				},
				{
					"name": "跨域策略请求"
				},
				{
					"name": "TCP协议相关基础知识"
				},
				{
					"name": "性能和安全的初步优化"
				},
				{
					"name": "常用的AJAX库和插件"
				}
			]
		},
		{
			"name": "底层原理和高阶JS函数",
			"children": [{
					"name": "函数柯里化"
				},
				{
					"name": "compos函数"
				},
				{
					"name": "惰性思想"
				},
				{
					"name": "组件插件封装"
				},
				{
					"name": "底层源码解读"
				}
			]
		}
	]
}, {
	"name": "前端工程化",
	"children": [{
			"name": "VUE全家桶",
			"children": [{
					"name": "基础知识"
				},
				{
					"name": "MVVM实现原理"
				},
				{
					"name": "路由处理"
				},
				{
					"name": "vuex公共状态管理"
				},
				{
					"name": "element-ui组件应用和二次封装"
				}
			]
		},
		{
			"name": "REACT全家桶",
			"children": [{
					"name": "基础知识"
				},
				{
					"name": "MVC实现原理"
				},
				{
					"name": "DOM DIFF"
				},
				{
					"name": "Virtual DOM"
				},
				{
					"name": "路由处理"
				},
				{
					"name": "公共状态管理",
					"children": [{
							"name": "REACT-REDUX、DAVS/SAGA等"
						},
						{
							"name": "compos函数"
						},
						{
							"name": "惰性思想"
						},
						{
							"name": "组件插件封装"
						},
						{
							"name": "底层源码解读"
						}
					]
				},
				{
					"name": "高阶租价"
				},
				{
					"name": "antd组件应用和二次封装"
				}
			]
		},
		{
			"name": "底层原理和高阶JS函数",
			"children": [{
					"name": "函数柯里化"
				},
				{
					"name": "compos函数"
				},
				{
					"name": "惰性思想"
				},
				{
					"name": "组件插件封装"
				},
				{
					"name": "底层源码解读"
				}
			]
		},
		{
			"name": "工程化开发部署",
			"children": [{
					"name": "webpack"
				},
				{
					"name": "git"
				},
				{
					"name": "linux"
				}
			]
		}
	]
}, {
	"name": "前端开发热门点",
	"children": [{
			"name": "TypeScript"
		},
		{
			"name": "flutter"
		},
		{
			"name": "react native"
		},
		{
			"name": "小程序"
		},
		{
			"name": "性能和安全的优化"
		}
	]
}]
  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值