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

问题

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

好比, 考虑以下嵌套的元素:函数

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

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

两个模型

在刚刚过去的那些糟糕年代, Netscape和M$对此有不一样的见解. spa

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

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

两种见解针锋相对事件

事件捕获(event capturing)

当使用事件捕获时ip

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

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

事件冒泡(event bubbling)

/ \
---------------| |-----------------
| outer        | |                |
|   -----------| |-----------     |
|   |inner     | |          |     |
|   -------------------------     |
|        Event BUBBLING           |
-----------------------------------

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

W3C 模型

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

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

而你做为开发者, 能够决定事件处理器是注册在捕获或者是冒泡阶段. 若是addEventListener的最后一个参数是true, 那么处理函数将在捕获阶段被触发; 不然(false), 会在冒泡阶段被触发.

例如以下的代码:

clipboard.png

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元素时, 以下事情发生了:

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

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

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

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

最后的结果是:

clipboard.png

若是咱们把祖先元素的事件处理器注册在冒泡阶段的话(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)

结果则是:

clipboard.png

传统模型

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元素.

相关文章
相关标签/搜索