资讯专栏INFORMATION COLUMN

JS中的事件顺序(事件捕获与冒泡)

张迁 / 1032人阅读

摘要:问题如果一个元素和它的祖先元素注册了同一类型的事件函数例如点击等那么当事件发生时事件函数调用的顺序是什么呢比如考虑如下嵌套的元素

问题

如果一个元素和它的祖先元素注册了同一类型的事件函数(例如点击等), 那么当事件发生时事件函数调用的顺序是什么呢?

比如, 考虑如下嵌套的元素:

-----------------------------------
| outer                           |
|   -------------------------     |
|   |inner                  |     |
|   -------------------------     |
|                                 |
-----------------------------------

两个元素都有onclick的处理函数. 如果用户点击了inner, innerouter上的事件处理函数都会被调用. 但谁先谁后呢?

两个模型

在刚刚过去的那些糟糕年代, Netscape和M$对此有不同的看法.

Netscape认为outer上的处理函数应该先被执行. 这被称作event capturing.

M$则认为inner上的处理函数具有执行优先权. 这被叫做event bubbling.

两种看法针锋相对

事件捕获(event capturing)

当使用事件捕获时

               | |
---------------| |-----------------
| outer        | |                |
|   -----------| |-----------     |
|   |inner      /          |     |
|   -------------------------     |
|        Event CAPTURING          |
-----------------------------------

outer上的事件处理器先触发, 然后是inner上的

事件冒泡(event bubbling)
               / 
---------------| |-----------------
| outer        | |                |
|   -----------| |-----------     |
|   |inner     | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------

与事件捕获相反, 当使用事件冒泡时, inner上的事件处理器先被触发, 其后是outer上面的

W3C 模型

W3C标准则取其折中方案. W3C事件模型中发生的任何事件, 先(从其祖先元素window)开始一路向下捕获, 直到达到目标元素, 其后再次从目标元素开始冒泡.

          1. 先从上往下捕获
                  |
                 | |  / 
-----------------| |--| |-----------------
| outer          | |  | |                |
|   -------------| |--| |-----------     |
|   |   inner     /  | |          |     |
|   |                  |           |     |
|   |   2. 到达目标元素后从下往上冒泡|     |
|   --------------------------------     |
|        W3C event model                 |
------------------------------------------

而你作为开发者, 可以决定事件处理器是注册在捕获或者是冒泡阶段. 如果addEventListener的最后一个参数是true, 那么处理函数将在捕获阶段被触发; 否则(false), 会在冒泡阶段被触发.

例如如下的代码:

        var selector = document.querySelector.bind(document);
        selector("div.outer").addEventListener("click", (e) => {
            selector("p:first-of-type").textContent += "outer clicked! "
        }, true)
        selector("div.inner").addEventListener("click", (e) => {
            selector("p:first-of-type").textContent += "inner clicked! "
        }, false)
        document.addEventListener("click", (e) => {
            selector("p:first-of-type").textContent += "document clicked! "
        }, true)

当点击inner元素时, 如下事情发生了:

点击事件开始于捕获阶段. 在此阶段, 浏览器会在inner的所有祖先元素上查找点击事件处理函数(从window开始).

结果找到了2个, 分别在documentouter上面, 而且这两个事件处理函数的useCapture选项为true, 说明它们是被注册在捕获阶段的. 于是, documentouter的点击处理函数被执行了.

继续向下寻找, 直到达到inner元素本身. 捕获阶段就此结束. 此时进入冒泡阶段, inner上的事件处理器得到执行.

事件命中目标元素后开始向上冒泡, 一路查找是否有注册了冒泡阶段的祖先元素上的事件处理器. 由于没有找到, 因此什么也没发生.

最后的结果是:

如果我们把祖先元素的事件处理器注册在冒泡阶段的话(addEventListeneruseCapture选项为false):

        var selector = document.querySelector.bind(document);
        selector("div.outer").addEventListener("click", (e) => {
            selector("p:first-of-type").textContent += "outer clicked! "
            console.log(e);
        }, false)
        selector("div.inner").addEventListener("click", (e) => {
            selector("p:first-of-type").textContent += "inner clicked! "
            console.log(e);
        }, false)
        document.addEventListener("click", (e) => {
            selector("p:first-of-type").textContent += "document clicked! "
        }, false)

结果则是:

传统模型

element.onclick = function(){}

将被注册在冒泡阶段.

事件冒泡的应用

例如: 当点击时的默认函数

如果在document上注册一个点击函数:

document.addEventlistener("click", (e) => {}, false)

那么任何元素上的点击事件最后都会冒泡到这个事件处理器上并触发函数 - 除非前面的事件处理函数阻止了冒泡(e.stopPropogation(), 在这种情况下事件不会继续向上冒泡)

注意: e.stopPropagation()只能阻止事件在冒泡阶段的向上传播. 如果被点击元素的祖先元素有注册在捕获阶段的事件处理器:

ancestorElem.addEventListner("click", (e) => {
    // do something...
    }, true)

那么该祖先元素上的事件处理器照样会在捕获阶段被触发.

因此, 你可以在document上设置这么一个处理函数, 当页面上的任何元素被点击时, 这个处理函数就被会触发. 一个实用的例子就是下拉菜单: 当点击文档上除下拉菜单本身时任意一处时, 下拉菜单会被隐藏.

在冒泡或者捕获阶段, e.currentTarget指向当前事件处理函数所附着的元素. 你也可以用事件处理函数内的this取而代之.

M$模型的麻烦

在M$模型中, 没有对e.currentTarget的支持, 更糟糕的是, this也不指向当前的HTML元素.

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/91135.html

相关文章

  • JS中的事件顺序(事件捕获冒泡)

    摘要:问题如果一个元素和它的祖先元素注册了同一类型的事件函数例如点击等那么当事件发生时事件函数调用的顺序是什么呢比如考虑如下嵌套的元素 问题 如果一个元素和它的祖先元素注册了同一类型的事件函数(例如点击等), 那么当事件发生时事件函数调用的顺序是什么呢? 比如, 考虑如下嵌套的元素: ----------------------------------- | outer ...

    ixlei 评论0 收藏0
  • JS事件冒泡捕获

    摘要:事件冒泡与事件捕获事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流事件发生顺序的问题。事件冒泡微软提出了名为事件冒泡的事件流。 事件冒泡与事件捕获 事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。考虑下面这段代码,就不写html->head,body之类的代码了,自行脑补 Click me! ...

    sixleaves 评论0 收藏0
  • JS 中的事件冒泡捕获

    摘要:在之前是只支持事件冒泡,包括之后和目前主流的浏览器都同时支持两种事件。中可以用来取消事件冒泡。 刚接触 JS 的那个时候,啥也不懂,只想着如何利用 Google、百度到的函数来解决实际的问题,不会想到去一探究竟。 渐渐的,对 JS 的语言的不断深入,有机会去了解一些原理性东西。最近在看 JQuery 源码,感触很多,总想着用原生的 JS 去实现自己的一个 JQuery 库。说实在的,J...

    Rindia 评论0 收藏0
  • js面试题

    摘要:面试题的基本数据类型和引用数据类型基本数据类型引用数据类型和有何区别表示一个对象被定义了,值为空值表示不存在这个值。 js面试题 JS的基本数据类型和引用数据类型 基本数据类型:undefined、null、boolean、number、string、symbol引用数据类型:object、array、function null 和 undefined 有何区别? null 表示一个对...

    Baoyuan 评论0 收藏0
  • 彻底搞懂JS事件冒泡捕获

    摘要:事件冒泡与事件捕获事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流事件发生顺序的问题。如下假设三层都有事件监听,这时我们点击的小的蓝方框,事件执行的顺序是怎么样的呢红黄蓝事件冒泡微软提出了名为事件冒泡的事件流。 事件冒泡与事件捕获 事件冒泡和事件捕获分别由微软和网景公司提出,这两个概念都是为了解决页面中事件流(事件发生顺序)的问题。 如下:假设三层div都...

    Half 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<