美文网首页
对于事件的理解

对于事件的理解

作者: 苍老师的眼泪 | 来源:发表于2022-01-18 13:56 被阅读0次

触发一个事件的过程有三个阶段:

  1. 捕获阶段(capture phase)
  2. 目标阶段(target phase)
  3. 冒泡阶段(bubbling phase)
    https://www.w3.org/TR/DOM-Level-3-Events/#event-flow
event.png

举个具体的例子:


image.png
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #a {
            width: 200px;
            height: 200px;
            background-color: coral;
        }

        #b {
            width: 100px;
            height: 100px;
            background-color: cornflowerblue;
        }
    </style>
</head>
<body>
    <div id="a">
        <div id="b">b</div>a
    </div>
    <script>
        let a = document.getElementById("a")

        a.addEventListener("click", function() {
            console.log('a was clicked')
        }, true)

        b.addEventListener("click", function(e) {
            console.log('b was clicked')
        })
    </script>
</body>
</html>

我们在a和b上面都注册了点击事件:
如果用addEventListener方法来注册的话,会有一个useCapture参数用于确定元素以哪个顺序接收事件,
true:在捕获阶段触发这个事件;
false: 在冒泡阶段触发这个事件;
默认为false,不在捕获阶段触发事件,而是在冒泡阶段再触发这个事件。

  • 当使用true作为参数时,也就是选择在捕获阶段触发该元素绑定的事件,我们点击b元素,事件从Window对象开始,
    沿着事件传播路径(事件经过的元素组成的路径)先来到a元素(因为a是b的父元素),此时事件还没有到达b,所以此时正处于冒泡阶段,
    于是此时会触发a事件,控制台会打印出a was clicked,然后再来到目标对象b,此时打印b was clicked,再进入冒泡阶段。

  • 相反如果我们采用默认的false参数,则是在冒泡阶段再触发事件。当事件对象经过a时,a不触发点击事件,等事件到达b,
    此时打印b was clicked, 然后事件进入冒泡阶段,冒回a时,此时才触发a的点击事件,打印a was clicked

假设我们想要在点击b的时候,不触发a也绑定了的相同事件(或者反过来点击a不触发b的相同事件)怎么办呢?
addEventListener的useCapture只用于决定在哪个事件阶段触发
相应的事件,而不能决定触不触发事件。
这时候需要用到 stopPropagation
该事件的作用是:阻止捕获和冒泡阶段中当前事件的进一步传播。
例如我们在b的事件处理函数中加入调用stopPropagation让事件不再继续传播

        a.addEventListener("click", function() {
            console.log('a was clicked')
        }, true)

        b.addEventListener("click", function(e) {
            console.log('b was clicked')
            e.stopPropagation()
        })

最后控制台还是打印
a was clicked
b was clicked
原因是我们给a的addEventListener函数的useCapture设置了true,它在事件捕获阶段就已经触发了它的事件,
把它设置为false,或不设置该参数即可达到相应的效果:只触发b事件,然后事件不再继续传播

关于stopPropagation的进一步说明:

假如我们有以下代码:

    <button id="btn">按钮</button>
    <script>
        let btn = document.getElementById('btn')
        btn.addEventListener('click', function() {
            console.log('第一次点击')
            } 
        )
        btn.addEventListener('click', function() {
            console.log('第二次点击')
            } 
        )
    </script>

我们想要在第一个添加的事件触发后,使得该事件不再继续传播,
也就是点击之后,只打印“第一次点击”,然后立马让事件停止传播,不再执行后续的事件处理函数
此时应该用stopImmediatePropagation而不是stopPropagation函数,但是这两个函数都不能阻止默认事件的发生,
要阻止默认事件的发生,使用preventDefault函数,比如选中复选框是点击复选框的默认行为,但是我们可以在
addEventListener事件处理函数中调用事件对象的preventDefault函数,阻止默认事件的发生

<body>
    <p>Please click on the checkbox control.</p>

    <form>
        <label for="id-checkbox">Checkbox:</label>
        <input type="checkbox" id="id-checkbox" />
    </form>

    <div id="output-box"></div>
    <script>
        document.querySelector("#id-checkbox").addEventListener("click", function (event) {
            document.getElementById("output-box").innerHTML += "Sorry! <code>preventDefault()</code> won't let you check this!<br>";
            event.preventDefault();
        }, false);
    </script>
</body>

相同,preventDefault也不决定了元素以哪个顺序接收事件

除此之外,还有一些概念和事件传播有着密切相关:
比如事件对象的target属性和currentTarget属性,
currentTarget: 当事件沿着 DOM 触发时事件的当前目标。它总是指向事件绑定的元素。
target: 事件触发的元素。我点击a那target就是a,点击b就是b

相关文章

网友评论

      本文标题:对于事件的理解

      本文链接:https://www.haomeiwen.com/subject/fxekhrtx.html