事件委托及树形菜单案例
详情展示√
事件传播机制:目标阶段和冒泡阶段
当前元素事件行为触发,从内到外所有祖先的事件行为触发,绑定方法依次执行。
概念:
鼠标滑过显示详情:95%都是通过css实现
做法一:
每个li下面都有一个自己的详情。每个详情都是当前盒子的子元素,鼠标滑过子元素显示。
做法二:事件委托做法:
把事件绑定给大盒子即最外层容器,这样不管后代元素中哪一个子元素触发了当前事件,由于事件冒泡传播机制,document.onmouseover都会执行,拿到事件源ev.target是xxx做xxx。
…… -> body -> html -> document -> window
click效果只能用事件委托来实现。事件委托就是冒泡传播的实战应用。
click才凸显出了事件委托的强大
轮播图改造(略)
不需要等待数据加载完再绑定
只要container存在,就能进行事件绑定。按照之前的做法,总要等待页面中的dom结构渲染完了再去绑事件。
事件委托:面试必问
给动态元素绑定事件√
事件这一块的重点:
-
事件委托
-
dom 2级事件绑定 事件池机制
能给动态元素绑定事件
let ul=document.querySelector('.list');
let button=document.querySelector('.add')
let liList=ul.children;
button.onclick=function(){
// 没点新增就加5个
let length=liList.length; // HtmlCollection是动态集合
let fragment=document.createDocumentFragment();
for(let i=0;i<5;i++){
let li=document.createElement('li');
let index=length+i+1;
li.innerText=`第${index}个li`
li.setAttribute('data-index',index);
fragment.append(li)
}
ul.append(fragment)
}
ul.onclick=function(ev){
if(ev.target.tagName==='LI'){
let li=ev.target;
alert(`我是第${li.getAttribute('data-index')}个li`)
}
}
https://www.jianshu.com/p/b0be2479d117
而且性能提高70%
总结
事件委托是基于事件冒泡传播机制完成。事件冒泡传播指的是说,若某个元素的某个事件行为触发,那么该事件会从当前元素自下而上 从内到外沿dom树依次往上传播,即当前元素的所有祖先当前事件行为都会被触发,若绑定方法则方法依次执行。
而事件委托指的就是,比如一个父元素容器内有很多子元素,我并不是给一个个子元素单独去绑定方法,而是把方法绑定在元素容器上。
使用事件委托的好处在于:比如在某些业务场景下,使用事件委托明显有很多优势。比如有些元素是动态创建出来的,给动态元素绑事件一般都可以用事件委托。我们就可以使用事件委托把方法绑定在这些元素的父容器上,没必要等待数据加载完再一个个给元素去绑方法。要具体区分是哪个子元素触发了事件,使用event.target获取事件源即可。
首先,性能会好很多。使用事件委托比原始一个个去绑性能高出50%左右。
还有,多元素绑事件时,业务逻辑很多时候是一体的,这个时候统一用事件委托处理也会更好。
树形结构菜单
Home [zTree -- jQuery tree plug-ins.]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>zTree</title>
<link rel="stylesheet" href="../css/zTreeStyle.css">
<style>
*{
margin: 0;
padding: 0;
box-sizing: border-box;
}
</style>
</head>
<body>
<ul class="ztree" id="ztree1"></ul>
<script src="../js/jquery-1.4.4.min.js"></script>
<script src="../js/jquery.ztree.all.min.js"></script>
<script>
let options={}
let treeData = [
{
name:"test1",
open:true,
children:[
{name:"test1_1"},
{name:"test1_2"},
{
name:'test_3',
children:[
{name:'test_child'}
]
}
]},
{name:"test2", open:true, children:[
{name:"test2_1"}, {name:"test2_2"}]}
];
$.fn.zTree.init($("#ztree1"), options, treeData)
</script>
</body>
</html>
事件委托:
基于时间冒泡传播机制,一个容器中很多元素的相关行为都要绑定方法,此时没必要一个个获得元素一个个绑方法,只要给父元素容器相关事件行为绑方法就好了。不管容器中哪一个子元素事件触发,由于冒泡传播机制,父级容器的该事件都会触发,方法也会被执行。执行方法中,通过event.target事件源判断做不同的事情。几个作用:
第1个作用,性能好。
第2个,有些业务逻辑用事件委托处理更好。比如,点啥啥干啥除此之外统一干什么事,用事件委托处理。
第3个作用,元素是动态绑定的。不用事件委托,数据加载完页面渲染完获取到才绑定方法;用委托。用事件委托不用获取元素,给父元素绑接口。
基于递归完成数据动态绑定
样式
bindHTML
基于事件委托实现切换
浏览器调试:f10 逐过程;f11逐语句
调试:
-
console.log
-
debug
count累加
bindHTML: 没有children了就到底了,再返回的时候count--。返回上一级的时候count--。
获取所有em,循环事件绑定?
一个容器里面如果很多元素都要做事件绑定,那我不需要一个一个去绑,使用事件委托。
$level0.on('click',function (ev) {
let target=ev.target,$target=$(target);
if(target.tagName==='EM'){
$target.toggleClass('open');
$target.next('ul.level').toggle()
}
})
封装为zTree插件
(function ($) {
function zTree(data) {
function bindHTML(result,level=1) {
let liStr=''
result.forEach(item=>{
const {name,children,open}=item;
liStr+=`<li>
<a class="title">${name}</a>
${children?`<em class="icon ${open?'open':''}"></em>`:''}`
if(children){
liStr+=`<ul class="level level${level}" style="display: ${open?'block':'none'}">${bindHTML(children,level+1)}</ul>`
}
liStr+=`</li>`
})
return liStr;
}
// 数据绑定
$(this).html(bindHTML(data));
// click事件
$(this).on('click',function (ev) {
let target=ev.target,$target=$(target);
if(target.tagName==='EM'){
$target.toggleClass('open');
$target.next('ul.level').toggle()
}
})
}
$.fn.extend({
zTree
})
})(jQuery)