我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

本文描述JavaScript的事件传递顺序,有capturebubble两种类型,对应的说的就是事件传递顺序。当多个嵌套元素都监控同一个事件的时候,就会发生事件传递顺序的问题。默认情况下,事件传递是bubble类型的,从里到外进行冒泡事件传递。通过更改addEventListeneruseCapture参数,可以修改事件传递顺序。事件的新顺序,是在一定范围内反向,而不是全部反向。

苏南大叔:解析JavaScript的event事件传递顺序和类型,如何阻止冒泡? - 事件传递顺序和类型
解析JavaScript的event事件传递顺序和类型,如何阻止冒泡?(图4-1)

苏南大叔的程序如此灵动博客,记录苏南大叔和计算机代码的故事。测试环境:谷歌浏览器。

e.eventPhase

关于冒泡的顺序问题,这里有个专用的变量.eventPhase用于进行表述区分。它的取值有以下几种:

.eventPhase官方说明含义
0none
1capturing从外到内反向
2target事件的直接作用对象
3bubbling默认从内到外冒泡
其它error
var doClick = function (e) {
    var level = "";
    switch (e.eventPhase) {
        case 0:
            level = "none";
            break;
        case 1:
            level = "capturing";
            break;
        case 2:
            level = "target";
            break;
        case 3:
            level = "bubbling";
            break;
        default:
            level = "error";
    }
    console.log(e.type, e.target.id, e.currentTarget.id, e.eventPhase, level);
    // click c1 c1 2 target
}

苏南大叔:解析JavaScript的event事件传递顺序和类型,如何阻止冒泡? - eventPhase
解析JavaScript的event事件传递顺序和类型,如何阻止冒泡?(图4-2)

区分targetcurrentTarget,可以参考:

useCapture

使用参数useCapture来控制是否在一定程度上反向,取值如下:

useCapture含义
true在目标上反向
false默认值冒泡

值得特别说明的是:只有三个参数都完全一致的时候,removeEventListener才能remove掉对应的事件。

addEventListener(event, function, useCapture)
removeEventListener(event, function, useCapture)

对于第三个参数位置useCapture,还可以表述为option对象,由于使用到的概率太小,所以这里不做描述。更多参数可以参考这里:

测试代码

<style>
    div {
        border: 1px solid gray;
        margin: 10px;
        padding: 10px;
        box-sizing: border-box;
    }
    #c0, #c2, #c4 {
        background-color: rgb(240, 201, 31);
    }
    #c1, #c3 {
        background-color: rgb(226, 223, 223);
    }
</style>
<div id="c0">
    <div id="c1">
        <div id="c2">
            <div id="c3">
                <div id="c4">
                    c4
                </div>
            </div>
        </div>
    </div>
</div>

事件监听:

var doClick = function (e) {
    var level = "";
    switch (e.eventPhase) {
        case 0:
            level = "none";
            break;
        case 1:
            level = "capturing";
            break;
        case 2:
            level = "target";
            break;
        case 3:
            level = "bubbling";
            break;
        default:
            level = "error";
    }
    console.log(e.type, e.target.id, e.currentTarget.id, e.eventPhase, level);
}
var c0 = document.getElementById("c0");
var c1 = document.getElementById("c1");
var c2 = document.getElementById("c2");
var c3 = document.getElementById("c3");
var c4 = document.getElementById("c4");
c0.addEventListener("click", doClick);
c1.addEventListener("click", doClick);
c2.addEventListener("click", doClick);
c3.addEventListener("click", doClick);
c4.addEventListener("click", doClick);

变化的就是最后几行代码,增加,true字样。例如:

c0.addEventListener("click", doClick, true);

测试结果

那么看一下这个的五层div的传递情况是怎么样的呢?

苏南大叔:解析JavaScript的event事件传递顺序和类型,如何阻止冒泡? - 运行截图
解析JavaScript的event事件传递顺序和类型,如何阻止冒泡?(图4-3)

默认情况下,冒泡向外传递事件,并不会向内传递(c0是最外层,c4是最内层)。所以,

  • 点击c0的时候,只有c0会收到事件,c1/c2/c3/c4不会接收到事件。
  • 点击c1的时候,只有c0/c1会收到事件,c2/c3/c4不会接收到事件。
  • 点击c2的时候,只有c0/c1/c2会收到事件,c3/c4不会接收到事件。
  • 点击c3的时候,只有c0/c1/c2/c3会收到事件,c4不会接收到事件。
c0c1c2c3c4点击事件顺序
falsefalsefalsefalsefalsec0c0
falsefalsefalsefalsefalsec1c1 => c0
falsefalsefalsefalsefalsec2c2 => c1 => c0
falsefalsefalsefalsefalsec3c3 => c2 => c1 => c0
falsefalsefalsefalsefalsec4c4 => c3 => c2 => c1 => c0
truefalsefalsefalsefalsec0c0
truefalsefalsefalsefalsec1c0 => c1
truefalsefalsefalsefalsec2c0 => c2 => c1
truefalsefalsefalsefalsec3c0 => c3 => c2 => c1
truefalsefalsefalsefalsec4c0 => c4 => c3 => c2 => c1
falsetruefalsefalsefalsec0c0
falsetruefalsefalsefalsec1c1 => c0
falsetruefalsefalsefalsec2c1 => c2 => c0
falsetruefalsefalsefalsec3c1 => c3 => c2 => c0
falsetruefalsefalsefalsec4c1 => c4 => c3 => c2 => c0
falsefalsetruefalsefalsec0c0
falsefalsetruefalsefalsec1c1 => c0
falsefalsetruefalsefalsec2c2 => c1 => c0
falsefalsetruefalsefalsec3c2 => c3 => c1 => c0
falsefalsetruefalsefalsec4c2 => c4 => c3 => c1 => c0
falsefalsefalsetruefalsec0c0
falsefalsefalsetruefalsec1c1 => c0
falsefalsefalsetruefalsec2c2 => c1 => c0
falsefalsefalsetruefalsec3c3 => c2 => c1 => c0
falsefalsefalsetruefalsec4c3 => c4 => c2 => c1 => c0
falsefalsefalsefalsetruec0c0
falsefalsefalsefalsetruec1c1 => c0
falsefalsefalsefalsetruec2c2 => c1 => c0
falsefalsefalsefalsetruec3c3 => c2 => c1 => c0
falsefalsefalsefalsetruec4c4 => c3 => c2 => c1 => c0
falsetruefalsetruefalsec0c0
falsetruefalsetruefalsec1c1 => c0
falsetruefalsetruefalsec2c1 => c2 => c0
falsetruefalsetruefalsec3c1 => c3 => c2 => c0
falsetruefalsetruefalsec4c1 => c3 => c4 => c2 => c0

结论是:

  • 设置useCapturetrue,只会影响其内部的元素被点击时的排序,不影响其自身及外部元素被点击时的排序。
  • 对于其内部的元素的点击,设置useCapturetrue的优先级高,越外层的优先级越高。

设置双向传递

对于同一个元素,可以同时设置向内和向外。就是说下面的函数定义是正常可以运行的。

e1.addEventListener("click", doClick, true);
e1.addEventListener("click", doClick, false);
c0c1c2c3c4点击事件顺序
双向falsefalsefalsefalsec0c0 => c0
双向falsefalsefalsefalsec1c0(逆) => c1 => c0(泡)
双向falsefalsefalsefalsec2c0(逆) => c2 => c1 => c0(泡)
双向falsefalsefalsefalsec3c0(逆) => c3 => c2 => c1 => c0(泡)
双向falsefalsefalsefalsec4c0(逆) => c4 => c3 => c2 => c1 => c0(泡)
false双向falsefalsefalsec0c0
false双向falsefalsefalsec1c1 => c1 => c0
false双向falsefalsefalsec2c1(逆) => c2 => c1(泡) => c0
false双向falsefalsefalsec3c1(逆) => c3 => c2 => c1(泡) => c0
false双向falsefalsefalsec4c1(逆) => c4 => c3 => c2 => c1(泡) => c0、

可见,如果对同一个元素设置双向事件,那么,就会把逆向的事件顺序提前(或者说凭空出现一个高优先级),而原有的冒泡事件顺序不变。

苏南大叔:解析JavaScript的event事件传递顺序和类型,如何阻止冒泡? - 双向截图
解析JavaScript的event事件传递顺序和类型,如何阻止冒泡?(图4-4)

阻止事件传递

阻止冒泡,阻止事件传递:

e.stopPropagation();

阻止默认行为:

e.preventDefault();

比如还是上面的五层div的例子,设置当c2的时候,阻止事件传递。

if(e.currentTarget.id=="c2"){
  e.stopPropagation();
}

那么,上述表格里面,所有的c2后面的传递顺序都不存在了(但是c2被传递到了)。

在这里苏南大叔发散一下思维:原本在冒泡顺序之外的元素,按理说可以通过.stopPropagation()把事件截至住。但是!如果外部的元素设置了useCapture,那么就可以强势插入事件顺序,甚至可以截获本来属于别人的事件。
比如click事件,如果不是写的a标签,而是注册click事件的话,那么,就存在着可能性来截获内部的点击事件了。

结束语

更多js相关经验文字,请点击:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   js