DOM 事件与事件委托
本文写于 2020 年 5 月 28 日
先思考一个问题:我们如何给一百个 button 添加点击事件?
遍历?
那岂不是要添加一百个监听器?
这就需要事件委托了。
其实这根本不是一个很难的概念,看下去,本文并不长。
1. 点击事件
现在我们拥有三个元素,他们嵌套成为爷爷、爸爸、儿子:
<div class='grandpa'>
<div class='father'>
<div class='son'></div>
</div>
</div>
然后分别给他们仨添加不同的三个事件监听。
因为事件冒泡,我们能知道他们都会执行,并且会按照一定顺序执行。
但是不同浏览器的顺序是不一样的。
IE 认为应该调用 .son
的事件,网景认为应该调用 .grandpa
。
后来 2002 年,W3C 发布了标准:
- 先按照从外向内——事件捕获;
- 在按照从内向外——事件冒泡。
整个过程就是——有监听函数就调用,没有就跳过。
开发者可以自己选择把最外层的事件,放在捕获阶段还是冒泡阶段。
如何选择呢?其实就是我们最熟悉的addEventListener
。
我们经常会用它去绑定事件,我们一般都只会传入两个参数。
可实际上这个函数有 3 个参数。
我们可以在第三个参数,放置一个布尔值,例如:xxx.addEventListener('click', fn, bool)
如果是true
,则是捕获方式(从外向内);如果不写,或者是falsy
值,则是冒泡(从内向外)。
顺序问题
上面说了,先捕获后冒泡。
那我同时给一个元素,先挂一个冒泡,再挂一个捕获——谁先触发?
谁先写,谁先触发,因为他们是同级的!
可以取消吗?
捕获不可以取消,但是冒泡可以取消。(有些事件不能取消,比如滚动事件)
e.stopPropagation()
中断冒泡。
2. 事件委托
回到我们刚开始的问题:我们如何给一百个 button 添加点击事件?
肯定不是遍历。
我们可以直接给父元素添加事件,例如:
father.addEventListener('click', e => {
console.log(e.target)
console.log(e.currentTarget)
})
这里看到,我们传入了一个 e,这个 e 可以尝试打印出来,就会发现其实是MouseEvent
。
这是一个对象,里面有各种各样的数据,比如我们需要的target
。
target
就是用户所点击的元素!
也就是说如果 father 有 10 个孩子,从 child1 一直到 child10,我们点谁,这个 target 就是谁。
而e.currentTarget
呢,和e.target
是有区别的!
简单来说呢 target
是被操作的元素,currentTarget
是被监听的元素。
在这个例子中,currentTarget
恒为 father,target
则会随着点击发生改变。
最后说一下,JS 其实是不支持事件的!支持事件的是浏览器,addEventListener
是浏览器的 DOM 提供的。
(完)