功能效果图:
思路:
- 在无限极别的菜单中,数据一般不是固定的,需要从服务器请求获取:
- 如果数据量不大,一般是一次请求数据,获取到所有级别的信息
- 如果数据量比较大,一般只是请求当前这一级的信息,点击展示的时候,再去请求下一级信息(再去动态生成下一级的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": "性能和安全的优化"
}
]
}]