JavaScript 事件代理
jQuery 曾经存在 3 种绑定事件的方法:bind / live / delegate,后来 live 被砍掉了,只留下 bind 与 delegate,它们之间的区别是,通过 bind 方法绑定的事件,只对当前存在的元素生效,而通过 delegate 则可以绑定“现在”以及“将来”的所有元素。
为“将来”元素绑定事件的适用场景还是挺多的。比如一个列表,或者一个表格,它可能会动态地被插入或者移除一些子元素,然后每个元素都需要有一个点击事件,这样的话我们就需要保证“现在”已存在的元素以及“将来”可能被添加进去的元素都能够正常工作。怎么办呢,我们总不能每插入一个元素就给它绑一次事件吧(事实上我以前没少干这事),因此 jQuery 就为我们提供了后者的方法。
一开始我觉得很奇怪,像 delegate 这样的方法是怎么实现的呢?通过监听 DOM 树变化吗?性能开销会不会特别大?后来知道了 JavaScript 有一种机制叫事件代理(event delegation),也就是本文要说的东西,才明白,原来一切都很简单。
事件代理及其工作原理
何为代理呢,大概就是,你把你要做的事情告诉我,我帮你做,我就是你的代理。
那么事件代理,顾名思义,在一个元素上触发的事件,由另一个元素去处理,后者就是前者的事件代理。
大概就是这么回事。那么,如何实现呢?
这里就涉及两个关于 JavaScript 事件的知识:事件冒泡(event bubbling)以及目标元素(target element):
- 当一个元素上触发事件的时候,比如说鼠标点击了一个按钮,同样的事件将会在它的所有祖先元素上触发。这就是所谓的事件冒泡。
- 所有事件的目标元素都将是最原始触发它的那个特定元素,就比如说那个按钮,其引用将被储存在事件对象内。
因此,我们可以做到:
- 给一个节点绑定事件
- 等待其子孙节点的冒泡事件
- 判断事件实际来源
- 做出相应处理
这就是事件代理的工作原理。
有什么用
一个典型的场景是,如果一个表格有 100 行 100 列,你需要给每一个单元格都添加点击事件,怎么办?
当然可以说一次性把它们全选出来,绑定事件不就完了。但是,内存 BOOM,浏览器 BOOM
用事件代理就简单多了,给 table 绑一次事件,然后等它们冒泡上来就行了。
还有就是动态添加的元素。比如某一时刻 table 被添加了一行,那么新的一行其事件同样能冒泡并且被 table 上的事件处理器接收到。
代码
Talk is cheap, show me the code.
//Some browser diff issue handler |