JS事件的三个阶段

327 阅读3分钟

主要有以下三个阶段:

  • 事件捕获:沿着windows向下传播直到目标事件触发位置(通常这个阶段就相当于预览,到底有哪些节点注册了事件呢,直到却不说)
  • 目标:到达目标事件触发位置开始执行事件
  • 冒泡:从目标事件开始逐级触发(沿着捕获的反方向逐级触发,就好像底层人民防抗,一层层来)

这时候你肯定会有疑问?这个过程到底能不能修改呢?答案当然是可以的,你可以选择在捕获阶段触发事件,也可以选择在某个阶段直接阻止事件的传播。

正常触发:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        html,body{
            border: 0;
            padding: 0;
            margin: 0;
        }
        #div1{
            width: 500px;
            height: 500px;
            background-color: rosybrown;
            position:relative ;
            font-size: 30px;
            text-align: center;
        }
        #div2{
            width: 350px;
            height: 350px;
            background-color: blue;
            position: absolute;
            top:50%;
            left:50%;
            margin-top: -175px;
            margin-left: -175px;

        }
        #div3{
            width: 200px;
            height: 200px;
            background-color: crimson;
            position: absolute;
            top:50%;
            left:50%;
            margin-top: -100px;
            margin-left: -100px;
            line-height: 200px;
        }
    </style>
</head>
<body>
<script>
    window.onload=function(){
        let div1 = document.querySelector('#div1');
        let div2 = document.querySelector('#div2');
        let div3 = document.querySelector('#div3');
        function fn(){
            console.log(this)
        }
        div1.addEventListener('click',fn,false);
        div2.addEventListener('click',fn,false);
        div3.addEventListener('click',fn,false);
    }

</script>
<div id="div1">div1
    <div id="div2">div2
        <div id="div3">div3</div>
    </div>
</div>
</div>
</body>
</html>


将事件改为捕获阶段来执行:

<script>
    window.onload=function(){
        let div1 = document.querySelector('#div1');
        let div2 = document.querySelector('#div2');
        let div3 = document.querySelector('#div3');
        function fn(){
            console.log(this)
        }
        // function fn(event) {
        //     console.log(event.target)
        // }

        div1.addEventListener('click',fn,true);
        div2.addEventListener('click',fn,true);
        div3.addEventListener('click',fn,true);
    }
其他部分代码均和上面一样,仅展示修改的js部分代码,当我们将addEventListener函数的第三个参数改为true时,则事件在捕获阶段就将执行


如果有的节点绑定的是冒泡,有的则是捕获,那么会怎样呢?这是一个很有趣的现象

div1、div3为true,div2为false 结果如图,思路就是有捕获的我会先执行捕获的,碰到冒泡的我再进行冒泡,同学们也可以自行尝试其他的情况


如果一个事件同时指定了捕获和冒泡两种行为怎么办呢?

他会按照事件注册的顺序来执行

node.addEventListener(
    'click',
    event => {
        console.log('冒泡')
    },
    false
)
node.addEventListener(
    'click',
    event => {
        console.log('捕获 ')
    },
    true
)
先冒泡后捕获

事件代理

什么是事件代理呢,就是当子元素是变动的节点时,我们经常将事件绑定在其父元素身上这种行为我们就称之为事件代理

   <script>
    <ul id="ul">
        <li>1</li>
        <li>2</li>
        <li>3</li>
        <li>4</li>
        <li>5</li>
        </ul>
        <script>
        let ul = document.querySelector('#ul')
    ul.addEventListener('click', (event) => {
        console.log(event.target);
    })
</script>
事件代理有两个好处:

  • 节省内存开销
  • 不需要注销事件

如何阻断事件的传播呢

  • stopPropagation  仅能够阻止这次事件的传播
  • stopImmediatePropagation     在阻止这次事件传播之外还可以阻止执行其他事件

请注意这里阻断的意思,并不是只当前事件不再传播了,而是整个事件都不再传播了。类似于给大河中间挖了一个沟,直接不让走了

stopPropagation

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        html,body{
            border: 0;
            padding: 0;
            margin: 0;
        }
        #div1{
            width: 500px;
            height: 500px;
            background-color: rosybrown;
            position:relative ;
            font-size: 30px;
            text-align: center;
        }
        #div2{
            width: 350px;
            height: 350px;
            background-color: blue;
            position: absolute;
            top:50%;
            left:50%;
            margin-top: -175px;
            margin-left: -175px;
            text-align: center;
            line-height: 350px;
        }

    </style>
</head>
<body>
<script>
    window.onload=function(){
        let div1 = document.querySelector('#div1');
        let div2 = document.querySelector('#div2');
        div1.addEventListener('click',function () {
            console.log(this)
        },false);
        div2.addEventListener('click',function (event) {
            console.log(this)
            event.stopPropagation()
        },false);

    }

</script>
<div id="div1">div1
    <div id="div2">div2
    </div>
</div>
</div>
</body>
</html>


stopImmediatePropagation

node.addEventListener(
    'click',
    event => {
        event.stopImmediatePropagation()
        console.log('冒泡')
    },
    false
)
// 点击 node 只会执行上面的函数,该函数不会执行
node.addEventListener(
    'click',
    event => {
        console.log('捕获 ')
    },
    true
)