事件委托,事件冒泡,事件委托

255 阅读4分钟

概念

在页面进行交互时,会触发一定的事件,在 javascript 中,我们可以通过一定的方式来捕获并处理这些事件。

DOM 树

在我们完成一个 html 或 xml 文档后,如果浏览器接受到一个页面请求响应后,浏览器会将文档进行解析,然后展开为结构化的树形 DOM 模型,并且定义每个节点的行为、所利用对象的方法和属性,以便更好的操作每个 DOM 节点。 image.png

事件流

事件流指的是从页面中获取事件的顺序,其中分为3个阶段:

  • 事件捕获阶段
  • 处于目标元素的阶段
  • 事件冒泡阶段

事件捕获

原理

当元素触发事件时,会从最外层的 document 节点开始,沿着 DOM 树逐层往下传递,然后触发相同类型的事件处理程序。例如:如果你点击了一个 button 他会先从 document 然后到 html、body、最后到达 button。如果途中存在着相同的事件类型,则会按顺序触发。

原理图

image.png

代码演示

    <div id="outer">
        <button id="myButton">点击我</button>
    </div>
    document.addEventListener('click', function () {
        console.log('document被点击');
    }, true);
    document.getElementById('outer').addEventListener('click', function () {
        console.log('outer div被点击');
    }, true);
    document.getElementById('myButton').addEventListener('click', function () {
        console.log('button被点击');
    }, true);
      输出顺序: 'document被点击' => 'outer div被点击' => 'button被点击'

事件冒泡

原理

当元素触发事件时,首先会在自身元素上触发对应的事件处理程序,然后沿着 DOm 树向上层逐步触发相同类型的事件处理程序。例如:如果你点击了一个 button 他会先从 button 然后到 body、html 最后到达 document。如果途中存在着相同的事件类型,则会按顺序触发。

原理图

image.png

代码演示

    <div id="outer">
        <button id="myButton">点击我</button>
    </div>
    ```
     document.getElementById('myButton').addEventListener('click', function () {
        console.log('button被点击');
    });
    document.getElementById('outer').addEventListener('click', function () {
        console.log('outer div被点击');
    });
    document.body.addEventListener('click', function () {
        console.log('body被点击');
    });
      输出顺序: 'button被点击' => 'outer div被点击' => 'body被点击'

事件委托

原理

事件委托运用了事件冒泡(大多数时候)或事件捕获的特性,通过将事件的监听器绑定到父级元素上,在目标元素触发事件时会向上冒泡,父级元素上的事件监听器就会捕获到该事件,然后通过传递过来的事件源 (evenr.target) 来判断是哪一个元素触发的事件,从而执行相应的逻辑。

原理图

image.png

代码演示

    <ul id="ulItem">
    	<li>1</li>
    	<li>2</li>
    	<li>3</li>
    </ul>
    let ulItem = document.getElementById('ulItem');
    ulItem.addEventListener('click',(e)=>{
    console.log(e.target.innerHTML)
    })
   点击 li-1 输出 1 
   点击第一个 li 时,会通过冒泡去执行 ul 中的 click 事件,并且 li 会被作为事件源传递到 ul 使得能够在 ul 中获取 li ,从而输出相应的内容

关于事件的知识补充

同个元素事件执行的顺序

在同个元素中我们可以通过两种方法进行事件绑定:

    1. onxxx方法 ,
    1. addEventListener方法

onxxx 方法

onxxx 方法只能对同一元素绑定一个同一类型的方法,例如: 对单一元素绑定多个 onclik 方法,则只会存在一个。其中在行块中绑定的事件与在js文本中绑定事件的执行顺序有所不同。

  • 行块中绑定事件: 当第一次绑定后,浏览器会认为该事件以绑定,则无法继续绑定。
<div id="divb" onclick="f1()" onclick="f2()">123</div>
let divb = document.getElementById('divb');
let f1 = ()=>{
  console.log('123')
}
let f2 = ()=>{
  console.log('321')
}

// 执行第一次绑定的事件处理程序 输出: 123
  • js文本中绑定事件: 当第二次绑定时,由于是在 js 文本中,此时第二个绑定会将第一个绑定覆盖。
let divb = document.getElementById('divb');
divb.onclick = ()=>{
        console.log('123')
}
divb.onclick = ()=>{
        console.log('321')
}

// 执行第二次绑定的事件处理程序 输出: 321

addEventListener 方法

addEventListener 方法能够对一个元素进行多次绑定,并且在 addEventListener 中,具有了更加灵活的控制方法。比如: 可以灵活的控制事件的触发时机是在捕获还是冒泡阶段触发

addEventListener 使用方法

addEventListener 方法能够传递三个参数。

  • type: 事件的类型,例如 click,mousemover 等
  • eventFuntion: 事件处理程序
  • options: 一个对象其中包含
    • capture(boolean): 指定事件是否在捕获阶段进行执行相应的处理
    • once(boolean): 指定事件是否只能触发一次
    • passive(boolean): 指定事件是否执行 preventDefault() 方法

绑定事件时可能会用到的一些 API