事件捕获、事件冒泡、事件委托,这三个类似又不尽相同的术语把我搞懵了很长一段时间,今天专门抽时间挨个看了一遍。设计模式
首先,是那个闻名遐迩的图浏览器
事件捕获和事件冒泡是事件流机制层面的东西,不以代码的意志为转移。函数
DOM2级规范(浏览器自身的事件规范)要求事件应该从document对象开始向下传播,找到具体的目标前,整个过程都是捕获阶段。性能
找到具体的目标后,开始向外层冒泡,直到回到document对象结束,这个过程叫冒泡阶段。spa
但规范只是规范,较旧的浏览器都是从window对象开始捕获事件的,建议放心地使用事件冒泡,特殊状况下再考虑事件捕获。设计
接下来是事件委托,这个才是重点!code
JS和HTML之间是靠事件来实现交互的,若是有多个div须要绑定click事件监听,不管是DOM0级和DOM2级,添加到页面上的事件处理程序数量将直接关系到页面的总体性能。对象
事件委托就是解决性能问题的,你能够把它看作是一种解决方案或设计模式。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对象添加一个事件处理程序,优势以下:
关于上面三点的最后一点,解释一下:若是咱们没有使用事件委托,那在具体元素好比某div上添加的监听,在该div被移除出DOM树后,绑在它身上的事件处理程序可能没法被垃圾回收,形成浪费。
每当将事件处理程序指定给元素时,运行中的浏览器代码与支持页面交互的JS代码之间会创建一个链接,这种链接越多,页面执行效率越低,利用事件委托能够尽量减小这种链接数量。呃……虽然可能也无法成功垃圾回收,但总体性能仍是更高的。