事件的捕获、冒泡和委托

事件捕获、事件冒泡、事件委托,这三个类似又不尽相同的术语把我搞懵了很长一段时间,今天专门抽时间挨个看了一遍。设计模式

首先,是那个闻名遐迩的图浏览器

clipboard.png

事件捕获和事件冒泡是事件流机制层面的东西,不以代码的意志为转移。函数

DOM2级规范(浏览器自身的事件规范)要求事件应该从document对象开始向下传播,找到具体的目标前,整个过程都是捕获阶段。性能

找到具体的目标后,开始向外层冒泡,直到回到document对象结束,这个过程叫冒泡阶段。spa

但规范只是规范,较旧的浏览器都是从window对象开始捕获事件的,建议放心地使用事件冒泡,特殊状况下再考虑事件捕获。设计

接下来是事件委托,这个才是重点!code

JS和HTML之间是靠事件来实现交互的,若是有多个div须要绑定click事件监听,不管是DOM0级和DOM2级,添加到页面上的事件处理程序数量将直接关系到页面的总体性能。对象

  • 首先,每一个函数都是对象,都会占内存。内存中的对象越多,性能就越差。
  • 其次,必须事先指定全部事件处理程序而致使的DOM访问次数,会延迟整个页面的交互就绪时间。也就是说在多个节点上挂载事件,会影响DOM的加载。

事件委托就是解决性能问题的,你能够把它看作是一种解决方案或设计模式。blog

事件委托利用了事件冒泡,只指定一个事件处理程序,就能够管理某一类型的全部事件。事件

少废话,上代码(如下例子来自《Javascript高级程序设计》,略删改)

// 面对这样一段HTML
<ul id="myLinks">
    <li id="goSomewhere"></li>
    <li id="doSomething"></li>
    <li id="sayHi"></li>
</ul>

不使用事件委托的笨办法:

var item1 = document.getElementById('goSomewhere')
var item2 = document.getElementById('doSomething')
var item3 = document.getElementById('sayHi')

item1.addEventListener('click', function(e) {
}, false)
item2.addEventListener('click', function(e) {
}, false)
item3.addEventListener('click', function(e) {
}, false)

使用事件委托,对于用户来讲最终的结果相同,但咱们其实只取了一个DOM元素,只添加了一个事件处理程序,但这种实现方案所须要占用的内存更少。

var list = document.getElementById('myLinks')

list.addEventListener('click', function(e) {
    var target = e.target;
    switch(target.id) {
        case 'goSomewhere':
            // TODO
            break;
        case 'doSomething':
            // TODO
            break;
        case 'sayHi':
            // TODO
            break;
    }
}, false)

若是可行的话,能够考虑为document对象添加一个事件处理程序,优势以下:

  • document对象在DOM树渲染的初期就已经准备完毕,无需等待DOMContentLoaded或load事件,并且document对象不像其余DOM节点那样会被移除。换句话说,只要目标元素呈如今页面上,就已经具有了事件处理程序。
  • 因为只引用一个DOM对象用来添加事件处理程序,在页面中设置事件处理程序所需的时间更少。
  • 整个页面占用的内存更少,提高总体性能。

关于上面三点的最后一点,解释一下:若是咱们没有使用事件委托,那在具体元素好比某div上添加的监听,在该div被移除出DOM树后,绑在它身上的事件处理程序可能没法被垃圾回收,形成浪费。

每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的JS代码之间会创建一个链接,这种链接越多,页面执行效率越低,利用事件委托能够尽量减小这种链接数量。呃……虽然可能也无法成功垃圾回收,但总体性能仍是更高的。

相关文章
相关标签/搜索