DOM 事件模型

357 阅读4分钟

什么是事件模型?

在遥远的以前,主流浏览器是微软公司和网景公司两家的。它们对一个问题产生了分歧。下面有一段代码,

<div class="father">
  <div class="son">我是儿子</div>
</div>

此时两个div元素都给点击事件绑定了监听函数,那么点击son的时候,算不算点击了father,毋庸置疑也是算的。那问题所在就是先执行谁的监听函数呢?

最终W3C出台了标准。要求所有浏览器同时支持捕获和冒泡两种调用顺序。

这就是事件模型,解决的问题就是监听函数调用顺序的问题。

捕获和冒泡

捕获阶段:从外向内找监听函数,找到了就执行,无则跳过。

冒泡阶段:从内向外找监听函数,找到了就执行,无则跳过。

所有事件触发都会走一遍捕获和冒泡的流程。即window->最里层->window

此时,开发者可以自行决定将监听函数加到什么阶段执行。

addEventListener()

该方法是DOM提供为元素的事件添加监听函数的API。

具体用法:element.addeventListener(事件名称,监听函数,options对象,加到什么阶段)

  1. 第一个参数是事件名称。由于事件较多,在这里就不一一赘述了。详情可参考MDN。事件参考
  2. 第二个参数是监听函数。当事件触发时,就会回调该函数。该函数接受一个参数event,参数包含了事件的相关信息。
  3. 第三个参数是options对象可以配置监听函数。比如监听函数只能调用一次。
  4. 第三个参数的类型是布尔值。不写默认为false,表示将监听函数加到冒泡阶段。true为加到捕获阶段。

图示:事件模型如何找到监听函数并执行

c45ed76e597ba9c1cfe7fc9c1623465.png

开发者通过设置addEventListener的参数自行决定将监听函数放到哪个阶段。

当为true时,就是捕获阶段,也就是图的左边(红色箭头部分)。
当为false时,就是冒泡阶段,也就是图的右边(绿色箭头部分)。

td元素有个监听函数tdListener,table有个监听函数tableListener。都放到捕获阶段。

当我们点击td时,浏览器会从window开始从外向内(到td结束,图示红色箭头部分)找监听函数,当发现table有监听函数,那么就会执行tableListener。然后td元素也发现监听函数就继续执行tdListener。此时,已经到最里层了,那么就从最里层开始从内向外(window结束)找监听函数(绿色箭头部分)。由于都没有监听函数,都跳过。 一个事件模型的流程就走完了。

阻止事件进一步传播 stopPropagation

event.stopPropagation();在监听函数上,调用参数的stopPropagation方法即可以阻断事件的传播。也即事件到此结束,不会继续找其他监听函数了。

很多人认为stopPropagation只能阻止冒泡的事件传播,其实MDN上解释的很清楚,代码运行效果也很清楚地告诉我们,该方法是可以连捕获阶段的事件传播也阻断的。不过确实由于监听函数大多数加到冒泡阶段,stopPropagation也往往只用于阻止冒泡了。

注意:所有事件的监听函数里都可以阻止事件进一步传播

阻止事件的默认行为

何为默认行为?当我们点击了a标签,a标签的点击事件会默认触发跳转行为。该行为就是默认行为。有时候我们并不想要这种行为则可以取消。

event.preventDefault();在监听函数上,调用event参数的preventDefault方法即可以阻止默认行为。但有几点需要注意:

  1. 该方法只是阻止了默认行为,事件传播不会有任何影响。
  2. 不是所有的事件都可以取消默认行为的。比如:scroll事件。

总结

当你了解了为什么要有事件模型,再去学会事半功倍。事件模型解决的是监听函数调用顺序的问题。有关的API也是紧紧围绕着这个问题。由于篇幅问题,本文章未讲事件委托(事件代理),其实它的原理也很简单就是利用了事件模型的运行机制,而解决了别的问题(给多个元素绑定事件和给目前不存在的元素绑定事件)。下篇文章我将详细讲讲。谢谢您的阅读!