如何注册事件回调

DOM的事件模型(事件机制)主要反映在事件的注册和监听。通常,我们会通过以下几种方式来注册事件回调:

  1. 通过HTML属性
  1. <button onclick="fn()">点击</button>
  1. 通过DOM元素属性
  1. myButton.onclick = function(e) {
  2. console.log('button click')
  3. }
  1. 通过addEventListener为元素绑定事件回调
  1. myButton.addEventListener('click',(e) => {
  2. console.log('button click!')
  3. })

但是现在不推荐使用HTML属性来设置事件回调,因为这种方式不能将内容/行为很好地分开,使得HTML变大并减少了可读性。

通过DOM元素属性的方式的缺点是每个事件元素只能被设置一个事件回调,在不需要设置多个事件回调的时候推荐使用这种办法,毕竟它比addEventListener少些很多个字符。

addEventListener
功能更高级,可以给DOM元素设置多个事件以及事件回调。但它也有个小缺点,IE6-8不兼容这个方法,在低版本的IE中需要使用attachEvent,为什么要说是小缺点呢,因为现在需要兼容IE的地方比较少了,甚至IE自己在自家最新的edge浏览器中都使用chrome的引擎了,而且如非必要,可以使用能解决跨浏览器兼容性的JS代码库来适应需求。

什么是DOM模型

知道如何绑定注册事件回调,更要清楚DOM的事件模型是怎样的

结论:DOM事件的模型(事件机制)就是事件捕获事件冒泡!这么简单?对,就是这么简单。

最开始还没有形成标准的时候,各家浏览器都有各自的事件流机制。事件冒泡是IE浏览器支持的机制,而事件捕获是NetScape(网景)支持的。

后面根据W3C在2002年发布的标准,规定浏览器应该同时支持事件冒泡和事件捕获,首先事件以捕获(爷爷 => 父亲 => 儿子)的方式传递,然后以冒泡(儿子 => 父亲 => 爷爷)的方式传递。它们传递的时候会看是否有监听函数,有则调用并提供信息,没有就跳过。

具体的过程看图:

DOM事件模型 - 图1

什么是事件流

事件传递的过程被称为事件流,它经历上面图中显示的三个阶段:捕获阶段、目标阶段、冒泡阶段。

事件的产生和传递一定按照捕获—> 目标 —>冒泡的方式执行吗?答案是否。

事件在开始调度之前,必须先确定事件对象的传播路径。即由程序员实现确定传播的方式是捕获还是冒泡。而且传播时的传播路径遵循HTML树结构。

程序员设置冒泡还是捕获通过addEventListener(eventType,listener,options)的第三个参数来设置,这个参数接收个可选参数对象:

  1. addEventListener('click',fn,{
  2. capture:true, // 默认是false,开启捕获设置capture为true
  3. once:true // 默认false,开启后listener参数只会被调用一次
  4. })

如果只想设置冒泡,可以直接传入一个true即可:

  1. addEventListener('click',fn,true)

捕获是不可被取消的,但冒泡可以:

  1. button.addEventListener('click',(e) => {
  2. console.log('button click')
  3. e.stopPropagation() // 这行代码可以阻止事件流的继续传递。
  4. })

一般用于封装特定需求的独立组件。

知道更多 —> addEventListener文档

更多

按照一般的逻辑,用户点击一个地方的时候,事件就应该从它开始往上传递,这更符合直觉,所以这也可能是W3C规定冒泡和捕获都支持,但函数addEventListener默认是冒泡的原因吧。

冒泡、捕获是一个非常有用的机制,我们可以用它来实现事件委托的功能。关于事件委托,将在下一篇博客介绍。