“代理”能够理解为代劳或者帮忙,或者相似你的事就是个人事这种乐于助人的良好品质。JavaScript 也有许多事件要处理,接着就演示下,如何在 DOM 元素中选出一位热心肠的好同窗。javascript
想象一个平平无奇的表格,像下面这样:html
需求是点击删除按钮可删除单条数据。java
用 JavaScript 实现点击删除的操做,须要监听按钮点击事件,在事件点击回调中,发送删除数据的请求,请求成功后从新渲染表格。函数
基本款的实现大概以下:性能
HTML:ui
...
<tr>
<td>1</td>
<td>汉堡</td>
<td>
<button data-id="1">删除</button>
</td>
</tr>
...
复制代码
JavaScript:this
const deleteItem = (id) => {
// 依据 ID 删除数据。
}
let btnList = document.getElementsByClassName('btn')
Array.prototype.forEach.call(btnList, (btn) => {
btn.addEventListener('click', function (e) {
deleteItem(this.dataset.id)
})
})
复制代码
上面的代码给每一条删除按钮都添加了点击事件的回调。上面的代码利用自定义属性 data-id
将单条数据的 ID
传给数据的删除按钮,而后在点击事件的回调中经过 dataset
属性取得,将其传递给处理删除的函数。spa
这样能够实现上面的上面所说的删除功能。prototype
但是这么写,当数据一多,将会对页面性能形成影响。设计
参考《JavaScript 高级程序设计》:
首先,每一个函数都是对象,都会占用内存;内存中的对象越多,性能就越差。其次,必须事先指定全部事件处理程序而致使的 DOM 访问次数,会延迟整个页面的交互就绪时间。
面对这种情况,解决方案是事件代理。
先看代码:
let table = document.getElementById('table')
table.addEventListener('click', function (e) {
if (e.target.tagName === 'BUTTON') {
deleteItem(e.target.dataset.id)
}
})
复制代码
上面的代码,首先在回调中经过事件对象 e
的 target
属性取得触发点击事件的元素,而后经过 tagName
判断这个元素是否是按钮,若是是的话,就进行删除操做。
只添加了一次事件处理函数,就实现了功能。
这里利用的是事件流的事件冒泡。
事件流有三个阶段,分别是事件捕获阶段,处于目标阶段,还有事件冒泡阶段。
按钮点击事件的事件流以下:
上图省略了 tr
,td
, tbody
这些元素,只用关键的元素来展现事件流。
“事件捕获”是不太精确的目标先接收到事件,首先是文档,而后再到 body
,逐渐精确到最具体的节点 button
.
当 button
接收到事件,即为“处于目标阶段”。
以后,事件再向外层逐级传播到不太具体的节点,这即是“事件冒泡阶段”。
假若有个外层元素称为 box
, 它里面包含了一个按钮 btn
:
给 box
和 btn
注册点击事件,代码以下:
box.addEventListener('click', function (e) {
console.log('box')
})
btn.addEventListener('click', function (e) {
console.log('button')
})
复制代码
点击 btn
:
能够看到,首先打印的是 'button'
,接着才是 box
. 由于 addEventListener
默认是在冒泡阶段执行回调函数的。这里,最具体的 btn
接受到事件以后,向外层冒泡,box
才能接受到事件。
改一下代码:
box.addEventListener('click', function (e) {
console.log('box')
}, true)
btn.addEventListener('click', function (e) {
console.log('button')
})
复制代码
这里给 box
的 addEventListener
传入了第三个参数 true
, 表示要在捕获阶段处理 box
的点击事件。
addEventListener
第三个参数形参为 useCapture
, 表示是否在捕获阶段处理事件,默认为 false
.
运行一下:
这样一改,首先打印的就是 'box'
, 由于事件具体节点是 btn
,而事件要从不太具体的节点,通过捕获阶段,才肯定到 btn
. 在捕获阶段,外层 box
要比 btn
早接受到事件,因此先执行了 box
捕获阶段的事件处理函数。
事件代理的代码中,注册点击事件的是 table
元素,发生点击事件最具体的节点是 button
. 事件在 button
发生后,因为冒泡,会传播到 table
, 从而触发 table
上事先注册的回调函数。因此,用事件冒泡实现事件代理,能够只给热心肠同窗 table
添加点击事件回调,而没必要劳烦表格中的每一个 button
.
在相似的场景下,可利用事件代理,来减小冗余,节约资源。