DOM 事件与事件委托

167 阅读3分钟

DOM 事件模型

事件模型描述的是从页面中接收事件的顺序。事件发生时会在元素节点与根节点之间按照特定的顺序传播,路径所经过的所有节点都会收到该事件,这个传播过程即 DOM 事件模型。

DOM 事件模型分为三个阶段:

  • 事件捕获阶段
  • 事件目标阶段
  • 事件冒泡阶段

image.png

事件捕获阶段

捕获阶段是从外到内传播即从根节点向最内侧节点传播。

事件捕获(event capturing)是由 Netscape 团队提出的一种事件模型,它认为不太具体的节点应该更早接受事件,而最具体的节点应该最后接受到事件,另一种说法就是由外向内找监听函数。

以上图为例,点击 td 元素后触发 click 事件的顺序是:

  1. Window
  2. Document
  3. html
  4. body
  5. table
  6. tbody
  7. tr
  8. td

事件目标阶段

在目标节点触发事件时,就处于事件目标阶段。

事件冒泡阶段

冒泡阶段就是从内向外传播直到根节点结束。

事件冒泡(event bubbling)是由 IE 团队提出的事件模型,它认为事件开始时由最具体的元素接受,然后逐级向上传播到不具体的节点,或者说由内向外找监听函数。

仍以上图为例,点击 td 元素后触发 click 事件的顺序是:

  1. td
  2. tr
  3. tbody
  4. table
  5. body
  6. html
  7. Document
  8. Window

DOM 事件模型的顺序

根据上述内容可知,Netscape 和 IE 各自提出的事件模型完全相反,所以为了统一,W3C 在 2002 年发布了标准(DOM Level 2 Events Specification),其中规定浏览器应同时支持两种调用顺序:在三个阶段中,首先经过事件捕获阶段,然后是实际目标接收到事件即处于目标阶段,最后再经过事件冒泡阶段,如上图所示。

当使用事件监听器(addEventListener)时,addEventListener 接收三个参数:第一个是绑定的事件类型;第二个是当事件被触发时要执行的函数;第三个是一个 Boolean 类型的可选参数,它决定了浏览器应该在哪个阶段(冒泡/捕获)发现元素有监听函数时去调用监听函数:

  • false 代表在冒泡阶段,默认为false
  • true 代码在捕获阶段
<div id="mapoTofu"></div>
<script>
    var mapoTofu = document.querySelector("#mapoTofu")
    mapoTofu.addEventListener('click',function(){
        console.log('在捕获阶段调用函数')
    }, true)
</script>

事件委托

事件委托(事件代理)是指将原本要绑定在目标元素的响应事件委托给父元素,让父元素来监听事件,其原理是 DOM 事件模型中的事件冒泡,当事件响应到目标元素上时,会通过事件冒泡机制从而触发外层父元素的绑定事件。

应用场景

例如有一个列表,其中有多个列表项,同时有一个按钮,点击按钮后会新增一个列表项,代码如下:

  • HTML
<ul id="mapoTofu">
    <li>0</li>
    <li>1</li>
    <li>2</li>
    <li>3</li>
    <li>4</li>
    <li>5</li>
</ul>

<button>点击添加一个 li 元素</button>

如果给每个 li 元素都绑定⼀个监听函数,那对于内存消耗是⾮常⼤的,同时无法对将来出现但当前不存在的 li 元素绑定监听函数,所以需要使用事件委托:

const list = document.querySelector("#mapoTofu")
list.onclick = function (event) {
    event = event || window.event
    const target = event.target
    if (target.nodeName.toLocaleLowerCase === 'li') {
        alert(target.innerHTML)
    }
}

优点

综上所述,事件委托有两个优点:

  • 节省内存
  • 可以监听动态元素