DOM 事件模型与事件委托

262 阅读3分钟

一、如何让 DOM 节点监听事件

事件的本质是程序各个组成部分之间的一种通信方式,也是异步编程的一种实现。

DOM 节点的事件操作(监听和触发),都定义在 EventTarget接口。所有节点对象都部署了这个接口。

该接口主要提供三个实例方法:

  • 绑定事件的监听函数:addEventListener()
  • 移除事件的监听函数:removeEventListener()
  • 触发事件:dispatchEvent()

EventTarget.addEventListener()

target.addEventListener(type, listener[, useCapture]);

用于在当前节点或对象上,定义一个特定事件的监听函数。一旦这个事件发生,就会执行监听函数。该方法没有返回值。

接受三个参数:

  • type:事件名称,大小写敏感
  • listener:监听函数。事件发生时,会调用该监听函数。
  • useCapture:该参数可选,布尔值。true表示监听函数将在捕获阶段触发。默认值为false(监听函数只在冒泡阶段被触发)。

第二个参数除了监听函数,还可以是一个具有handleEvent方法的对象,效果与监听函数一样。

第三个参数除了布尔值useCapture,还可以是一个监听器配置对象,定制事件监听行为:

element.addEventListener('click', function(event){
    // 只执行一次的代码
}, {once: true});

addEventListener()方法可以为同一对象的同一事件,添加多个不同的监听函数,这些函数将按照添加顺序触发,即先添加先触发。但如果为同一事件多次添加相同的监听函数,该函数只会执行一次,多余的添加将自动被去除。

监听函数内部的this,指向当前事件所在的那个对象

// HTML 代码如下
// <p id="para"> hello </p>

var para = document.getElementById('para');
para.addEventListener('click', function(e){
    console.log(this.nodeName); // this 指向事件所在的对象 para
});

二、事件模型

浏览器的事件模型,就是通过监听函数(listener)对事件做出反应。事件发生后,浏览器监听到了这个事件,就会执行对应的监听函数。这是事件驱动编程模式(event-driven)的主要编程方式。

1. 事件的传播

一个事件发生后,会在子元素和父元素之间传播(propagation),分为捕获阶段(capture phase),目标阶段(target phase),冒泡阶段(bubbling phase)。

这种三阶段的传播模型,使得同一个事件会在多个节点上触发。

0c4ee59560d749fd004a3a695e287ef.png

简而言之就是,事件捕获是从外向内找监听函数,事件冒泡是从内向外找监听函数。

2. 如何取消冒泡

捕获阶段不可取消,但冒泡可以取消。

e.stopPropagation()可以中断冒泡,浏览器不再向上走,一般用于封装某些独立的组件。

三、事件委托(事件代理)

事件代理(delegation):由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数定义在父节点上,由父节点的监听函数来统一处理多个子元素的事件。

即利用事件冒泡的机制,把里层所需要响应的事件绑定到外层。

var ul = document.querySelector('ul');

ul.addEventListener('click', function (event) {
  if (event.target.tagName.toLowerCase() === 'li') {
    // some code
  }
});

上面代码中,click事件的监听函数定义在<ul>节点上,它处理的是子节点<li>click事件。

这样做的好处是,不用在每个<li>节点上定义监听函数,只要在父节点上定义一个监听函数,就能处理多个子节点的事件,而且以后再添加子节点,监听函数依然有效。

另一种场景是,需要监听目前并不存在的元素的点击事件,同样也是把监听函数定义在父节点上,等点击后,冒泡阶段再判断是不是我想要监听的元素即可。

所以事件委托的优点:1. 省监听数(省内存);2. 可以监听动态元素。

参考链接

网道JavaScript教程

本文只是作为个人学习笔记整理记录。