今天看啥  ›  专栏  ›  〃不枉俗尘づ

事件流

〃不枉俗尘づ  · CSDN  ·  · 2020-10-30 20:15


前言

JS事件流问题也可以理解为事件触发顺序问题,而JS事件流最早要从IE和网景公司的浏览器大战说起,当浏览器发展到第四代时,IE提出的是冒泡流,而网景提出的是捕获流,两种事件流形式完全相反。


一、以前两种事件流分析

1.冒泡事件流

个人理解是和冒泡排序原理相似,即事件有最具体的接收,之后逐渐向上冒泡,传到页面。(即由子元素逐渐向父元素传)
以button按钮为例:

在这里插入图片描述

2.捕获事件流

与冒泡事件流原理完全相反,事件从页面元素开始接收,逐级向下到最具体的元素。
同样以button按钮为例:
在这里插入图片描述

结合:

后来在W3C组织的统一之下,JS支持了冒泡流和捕获流,两种事件流在DOM2级事件中得到了统一,相对于DOM0事件类型,DOM2级有以下特点:

  1. 允许某个元素绑定多个同类型事件(可以拿addEventLIstener事件与on事件为例)。
  2. 规定了事件流的三个阶段:捕获阶段,目标阶段,冒泡阶段。
    注意用Event.eventPhase可以返回当前所在的阶段:
var phase = event.eventPhase;
  • 1
  • 1

0,事件目前没有发生。
1,事件目前处于捕获阶段,即处于从祖先节点向目标节点的传播过程中。
2,事件到达目标节点,即Event.target属性指向的那个节点。
3,事件处于冒泡阶段,即处于从目标节点向祖先节点的反向传播过程中。


二、JS事件流

1.原理

JS事件的原理图如下:

在这里插入图片描述
由图可知:

  1. 现在的JS事件流是由捕获事件流和冒泡事件流组合形成的,即由window开始最后回到window的过程。
  2. 事件流被分为三个阶段(1-5)捕获过程、(5-6)目标过程、(6~10)冒泡过程。
  3. 这个过程决定了事件点击时的执行顺序。

1.on属性问题:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Document</title>
</head>
<body>
    <div style="width:200px;height:200px;background:lightblue" id="content">
        <button style="width:100px;height:50px;" id="btn1">
        </button>
    </div>
</body>
<script type="text/javascript">
    var content = document.getElementById("content");
    var btn1 = document.getElementById('btn1');
    btn1.onclick = function(){
        alert("button");
    };
    content.onclick = function(){
        alert("content");
    }
    //点击button的结果是先打印button之后再打印content
</script>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

由执行结果可知,on-属性的执行是在冒泡阶段发生的,对于EventTarget.addEventListener()的原理也是一样的。

2.addEventLIstener()问题

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset = "UTF-8">
    <title>Document</title>
</head>
<body>
    <div style = "width:200px;height:200px;background:lightblue" id="content">
        <button style = "width:100px;height:50px;" id="btn1">
        </button>
    </div>
</body>
<script type = "text/javascript">
    var content = document.getElementById("content");
    var btn1 = document.getElementById('btn1');
   btn1.addEventListener('click', function () {  alert("button的冒泡阶段"); }, false);
   btn1.addEventListener('click', function () {  alert("button的捕获阶段"); }, true);
   content.addEventListener('click', function () {  alert('content的冒泡阶段'); }, false);
   content.addEventListener('click', function () {  alert('content的捕获阶段'); }, true);
   //结果为:content的捕获阶段
   //       button的冒泡阶段
   //        button的捕获阶段
   //        content的冒泡阶段
</script>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

注意EventTarget.addEventListener()监听函数useCapture值为false时表示只在冒泡阶段被触发


三、阻止事件传递

通过之前的描述可以轻易看出,事件流中某元素被触发之后会造成连锁反应:会导致其容器元素同类型的事件被触发,有时候,这并不是我们所需要的所以,我们需要进行阻止。

1.Event.stopPropagation() 方法

stopPropagation()方法可以阻止事件在 DOM 中继续传播,防止再触发定义在别的节点上的监听函数,但是不包括在 当前节点 上其他的事件监听函数。
上代码:

function stopEvent(e) {
  e.stopPropagation();
}

el.addEventListener('click', stopEvent, false);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

此时,click事件将不会进一步冒泡到el节点的父节点。

2.Event.stopImmediatePropagation()方法

此方法与之前的方法最大的区别在于其阻止的更加彻底,可以阻止同一个事件的其他监听函数被调用,不管监听函数定义在当前节点还是其他节点。
如果同一个节点对于同一个事件指定了多个监听函数,这些函数会根据添加的顺序依次调用。只要其中有一个监听函数调用了Event.stopImmediatePropagation()方法,其他的监听函数就不会再执行了。
上代码:

function l1(e){
  e.stopImmediatePropagation();
}

function l2(e){
  console.log('hello world');
}

el.addEventListener('click', l1, false);
el.addEventListener('click', l2, false);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上面代码在el节点上,为click事件添加了两个监听函数l1和l2。由于l1调用了event.stopImmediatePropagation方法,所以l2不会被调用。

3.cancelBubble

cancelBubble是IE标准下阻止事件传递的属性,设置cancelBubble=true表示阻止冒泡。
它与stopPropagation()兼容性对比如下:
在这里插入图片描述
所以,注意:

  1. 若需要在捕获阶段阻止事件传递,使用addEventListener()进行事件监听,同时使用e.stopPropagation()阻止冒泡。
  2. 若需要在冒泡阶段阻止事件传递,在兼容addEventListener()和attachEvent()的同时,为保险起见,需要对阻止事件传递方式进行兼容。以下仅表示在DOM2及事件模型下兼容方式:
if (e.stopPropagation){
      e.stopPropagation();
  }else{
      e.cancelBubble = true;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5



原文地址:访问原文地址
快照地址: 访问文章快照