事件代理(也称事件委托)事件代理,俗地来讲,就是把⼀个元素响应事件 ( click 、 keydown ......)的函数委托到另⼀个元素 前⾯讲到,事件流的都会经过三个阶段: 捕获阶段 -> ⽬标阶段 -> 冒泡阶段,⽽事件委托就是在冒泡阶段完成
事件委托 会把⼀个或者⼀组元素的事件委托到它的⽗层或者更外层元素上,真正绑定事件的是外层元 素,⽽不是⽬标元素 当事件响应到⽬标元素上时,会通过事件冒泡机制从⽽触发它的外层元素的绑定事件上,然后在外层元 素上去执⾏函数
应用场景
如果我们有⼀个列表,列表之中有⼤量的列表项,我们需要在点击列表项的时候响应⼀个事件
<ul id="list">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
......
<li>item n</li>
</ul>
如果给每个列表项⼀⼀都绑定⼀个函数,那对于内存消耗是⾮常⼤的
// 获取⽬标元素
const lis = document.getElementsByTagName("li")
// 循环遍历绑定事件
for (let i = 0; i < lis.length; i++) {
lis[i].onclick = function(e){
console.log(e.target.innerHTML)
}
}
这时候就可以事件委托,把点击事件绑定在⽗级元素 ul 上⾯,然后执⾏事件的时候再去匹配⽬标元素
// 给⽗层元素绑定事件
document.getElementById('list').addEventListener('click', function (e) {
// 兼容性处理
var event = e || window.event;
var target = event.target || event.srcElement;
// 判断是否匹配⽬标元素
if (target.nodeName.toLocaleLowerCase === 'li') {
console.log('the content is: ', target.innerHTML);
}
});
还有⼀种场景是上述列表项并不多,我们给每个列表项都绑定了事件
但是如果⽤户能够随时动态的增加或者去除列表项元素,那么在每⼀次改变的时候都需要重新给新增的 元素绑定事件,给即将删去的元素解绑事件
如果⽤了事件委托就没有这种麻烦了,因为事件是绑定在⽗层的,和⽬标元素的增减是没有关系的,执 ⾏到⽬标元素是在真正响应执⾏事件函数的过程中去匹配的
举个例⼦: 下⾯ html 结构中,点击 input 可以动态添加元素
<input type="button" name="" id="btn" value="添加" />
<ul id="ul1">
<li>item 1</li>
<li>item 2</li>
<li>item 3</li>
<li>item 4</li>
</ul>
使用事件委托
const oBtn = document.getElementById("btn");
const oUl = document.getElementById("ul1");
const num = 4;
//事件委托,添加的⼦元素也有事件
oUl.onclick = function (ev) {
ev = ev || window.event;
const target = ev.target || ev.srcElement;
if (target.nodeName.toLowerCase() == 'li') {
console.log('the content is: ', target.innerHTML);
}
};
//添加新节点
oBtn.onclick = function () {
num++;
const oLi = document.createElement('li');
oLi.innerHTML = `item ${num}`;
oUl.appendChild(oLi);
};
可以看到,使⽤事件委托,在动态绑定事件的情况下是可以减少很多重复⼯作的
总结:
适合事件委托的事件有: click , mousedown , mouseup , keydown , keyup , keypress 从上⾯应⽤场景中,我们就可以看到使⽤事件委托存在两⼤优点:
- 减少整个⻚⾯所需的内存,提升整体性能
- 动态绑定,减少重复⼯作
但是使⽤事件委托也是存在局限性:
- focus 、 blur 这些事件没有事件冒泡机制,所以⽆法进⾏委托绑定事件
- mousemove 、 mouseout 这样的事件,虽然有事件冒泡,但是只能不断通过位置去计算定位, 对性能消耗⾼,因此也是不适合于事件委托的
如果把所有事件都⽤事件代理,可能会出现事件误判,即本不该被触发的事件被绑定上了事件