一、事件委托
1. 什么是事件委托
在 JavaScript 中,事件委托(delegate)也称为事件托管或事件代理,就是把目标节点的事件绑定到祖先节点上。这种简单而优雅的事件注册方式是基于事件传播过程中,逐层冒泡总能被祖先节点捕获。
这样做的好处:优化代码,提升运行性能,真正把 HTML 和 JavaScript 分离,也能防止出现在动态添加或删除节点过程中注册的事件丢失的现象。
2. 为什么要用事件委托
1)节省监听数,也就是节省内存
设想一个场景,我们要开发一个网页版扫雷程序,网页上有一个id为lei的<div>
,<div>
里嵌套有20*20=400个<button>
。
<div id="lei">
<button>口</button>
<button>口</button>
<!-- 一共400个button -->
<button>口</button>
</div>
那么,如何为这400个<button>
添加click
事件呢?
方案1:遍历<div>
的全部<button>
,给每一个<button>
添加click
事件,也就是当前页面会同时存在400个事件。(差评)
方案2:为<div>
添加一个事件委托,代码如下:
lei.addEventListener('click', (e) => {
const t = e.target;
if (t.tagName.toLowerCase() === 'button') {
console.log('button被点击');
}
});
上述代码中,事件的监听函数定义在<div>
节点,但实际上,它处理的是子节点<button>
的click
事件。显然,方案2比方案1会节约大量的资源。
2)可以监听动态元素
那么,如果刚才的<div>
里面的<button>
并不是事先写好的HTML,而是js动态生成的呢?
//通过js动态添加button到div
function generateButtons() {
for (let i = 0; i < 400; i++) {
const btn = document.createElement('button');
lei.appendChild(btn);
}
}
这种情况,只能通过事件委托来监听<div>
节点,以实现为<button>
添加处理click
事件的功能。
二、阻止默认动作
浏览器对于一些事件会触发默认动作,比如点击<a>
标签会跳转到href
指向的网页,那么如何阻止这个默认动作呢?
在支持addEventListener()
的浏览器中,可以通过调用事件对象的preventDefault()
方法取消事件的默认操作。IE9之前的IE中,可以通过设置事件对象的returnValue
属性为false
达到同样的效果。下面一段代码是结合三种技术取消事件:
function cancelHandler(event) {
var event = event || window.event;//兼容IE
//取消事件相关的默认行为
if(event.preventDefault) { //标准技术
event.preventDefault();
}
if(event.returnValue) { //兼容IE9之前的IE
event.returnValue = false;
}
return false; //用于处理使用对象属性注册的处理程序
}
三、阻止事件冒泡
1. 什么是事件冒泡?
举个例子:
<div id="p" style="width: 200px; height: 200px; background-color: red;">
<div id="c" style="width: 100px; height: 100px; background-color: blue;"></div>
</div>
为上面嵌套的两个div,分别添加onclick事件
p.onclick = () => console.log('p is clicked');
c.onclick = () => console.log('c is clicked');
1)在鼠标点击外层红色的<div>
时,控制台输出
p is clicked
2)在鼠标点击内层蓝色的<div>
时,控制台输出
c is clicked
p is clicked
2. 事件冒泡的概念
浏览器从用户点击的按钮从下往上遍历至 window,逐个触发事件处理函数。
W3C 事件模型/事件机制:对每个事件先捕获再冒泡。
3. 如何阻止事件冒泡
1)event.stopPropagation()
我们来改写刚才js代码的c.onclick部分
c.onclick = (event) => {
console.log('c is clicked');
event.stopPropagation();
};
在c的onclick
代码加上一个参数event
,并在代码最后一行加上event.stopPropagation();
通过这种方式,就可以达到阻止事件冒泡的目的:控制台输出c is clicked后不会再输出p is clicked。
注意,event.stopPropagation()
并不会阻止默认动作。
2)return false
c.onclick = () => {
console.log('c is clicked');
return false;
};
通过return false
的方式,既阻止了事件冒泡,也阻止了默认行为。