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

2019-12-31  本文已影响0人  明里人
问题:

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

<div class="outer">
    <div class="inner"></div>
</div>

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

事件流:

事件流描述的是从页面中接受事件的顺序,但有意思的是,微软(IE)和网景(Netscape)开发团队居然提出了两个截然相反的事件流概念,IE的事件流是事件冒泡流(event bubbling),而Netscape的事件流是事件捕获流(event capturing)。

事件捕获(event capturing)

网景公司提出的事件流叫事件捕获流。

事件捕获流的思想是不太具体的DOM节点应该更早接收到事件,而最具体的节点应该最后接收到事件,针对上面同样的例子,点击按钮,那么此时click事件会按照这样传播:(下面我们就借用addEventListener的第三个参数来模拟事件捕获流)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <div>
        <button>
            <p>点击捕获</p>
        </button>
    </div>
    <script>
        var oP=document.querySelector('p');
        var oB=document.querySelector('button');
        var oD=document.querySelector('div');
        var oBody=document.querySelector('body');

        oP.addEventListener('click',function(){
            console.log('p标签被点击')
        },true);
    
        oB.addEventListener('click',function(){
            console.log("button被点击")
        },true);

        oD.addEventListener('click',  function(){
            console.log('div被点击')
        },true);

        oBody.addEventListener('click',function(){
            console.log('body被点击')
        },true);
    </script>
</body>
</html>

同样我们看一下后台的打印结果:


image.png

正如我们看到的,它是和冒泡流万全相反,从最不具体的元素接收到最具体的元素接收事件 body=>div=>button=>p

事件冒泡(event bubbling)

与事件捕获相反, 事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的节点

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body onclick="bodyClick()">
    <div onclick="divClick()">
        <button onclick="btn()">
            <p onclick="p()">点击冒泡</p>
        </button>
    </div>
    <script>
       function p(){
          console.log('p标签被点击')
       }
        function btn(){
            console.log("button被点击")
        }
         function divClick(event){
             console.log('div被点击');
         }
        function bodyClick(){
            console.log('body被点击')
        }
    </script>
</body>
</html>

点击p元素打印结果:


image.png
DOM事件流:

DOM事件分0级和2级:
1、0级分两种:

<input type="button" id="btn" value="按钮" onclick="alert('123')">
document.getElementById("btn").onclick = function () {
    alert('123');
}
事件没有1级DOM

2、DOM2级事件:添加和移除事件处理程序:addEventListener()和removeEventListener()。

DOM2级(addEventListener)事件流包含3个阶段,事件捕获阶段、处于目标阶段、事件冒泡阶段。 先(document)开始一路向下捕获, 直到达到目标元素, 其后再次从目标元素开始冒泡.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
    <button id="btn">DOM事件流</button>
    <script>
        var btn=document.getElementById("btn");
        btn.onclick=function(event){
            console.log("div 处于目标阶段");
        };
        document.body.addEventListener("click",function(event){
            console.log("event bubble 事件冒泡");
        },false);
        document.body.addEventListener("click",function(event){
            console.log("event catch 事件捕获");
        },true);
    </script>
</body>
</html>

打印结果为:


image.png

就是这样一个流程,先捕获,然后处理,然后再冒泡出去。

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

阻止冒泡事件:

w3c的方法是e.stopPropagation(),IE则是使用e.cancelBubble = true

参考链接: 浅谈js的事件冒泡和事件捕获

上一篇 下一篇

猜你喜欢

热点阅读