浅谈:DOM事件模型及事件委托
2018-03-24
前戏
阅读本文需要:
- 知道有 .addEventListener() 这样一个添加事件监听的函数。
正文
事件模型
思考:假设页面中有这样三个元素:
1 | <div id="div1"> |
为这三个元素添加点击事件:
1 | div1.addEventListener('click', () => { console.log('div1被点击了') }) |
那么如果div3被点击,会执行哪个元素的点击事件呢?
这里看起来似乎是div3将他的父元素都覆盖了,所以只会触发div3的事件。然而div3仍然在div1和div2的范围内,从这种角度看应该算作所有的元素都被点击了。而且如果所有元素的点击事件都会被触发,那么事件的执行顺序又是怎样的。
哪种观点来看似乎都没有什么问题,但大家都需要有一个统一的标准,所以DOM就提供了这样的标准,也就是 事件模型 。
当一个事件发生时,这个事件会在window对象和目标DOM节点之间传播,传播过程中如果遇到DOM节点身上有添加的监听事件就会执行。整个传播过程分为三个阶段:
- 捕获阶段:从window对象向目标节点传播。
- 目标阶段:在目标节点触发。
- 冒泡阶段:从目标节点回到window对象。
DOM节点通过.addEventListener()函数添加事件监听,这个函数除了接收事件类型和执行函数,还可以接收一个参数以指定事件监听的传播阶段,默认值为false也就是冒泡阶段,可以修改为true来使事件在捕获阶段被触发。
那么上文的例子中事件的执行顺序就应该是 3 -> 2 -> 1 。
需要注意的是如果目标元素添加了多个监听事件,那么当它被点击时目标阶段的事件执行顺序是由代码位置决定的,而不取决于事件的捕获与冒泡。
事件委托
如果页面中存在一个元素,这个元素有许多相似的子元素,这些子元素都需要监听自身的点击事件,例如:
1 | <ul id="list"> |
那么为了给这些元素添加监听事件,就需要为每个元素分别添加一个事件监听:
1 | for (let i = 0; i < list.children.length; i++) { |
看起来还ok,但实际上这里大部分工作都是无意义的重复动作。如果页面中有100个或者1000个这样的元素,仍然需要分别为每个元素添加事件监听,这就会造成很大的浪费。因为每个响应都是一样的,那么就没必要为这些元素一个一个添加监听,利用事件会在冒泡阶段传播到父元素的特性,可以直接在父元素添加一个监听事件,那么它的子元素每次被点击时,都会通知它自身。这样就能通过这个父元素统一监听和处理这些子元素的事件,这种方法就叫做 事件委托 。
例如:
1 | list.addEventListener('click', (event) => { |
那么事件委托的优点就是提高性能了,毕竟只需要添加一次事件监听。
结语
- 写结语了吗?
- 没有哦。