js事件传播机制

108 阅读3分钟

前置概念:

在一个相互嵌套的父子dom元素中,给父子元素都绑定了【相同事件类型】的事件时,事件如何触发,由哪些dom元素触发?这些一系列的触发存在一套规则,这套规则便是js的事件传播机制。

案例

以下就是一个多级嵌套的html元素

    <div class="father">
        <div class="son">
            <div class="sun"></div>
        </div>
    </div>

给三个div都绑定点击事件

        const divs = document.getElementsByTagName('div')
        divs[0].addEventListener('click', function () {
            console.log('father');
        })
        divs[1].addEventListener('click', function () {
            console.log('son');
        })
        divs[2].addEventListener('click', function () {
            console.log('sun');
        })

当我们点击sun这个div标签时,不论我们的dom元素在页面中是如何布局,三个点击事件都将触发。

注意!!! 事件触发的规则和页面布局毫无关系,只和我们的【html中的嵌套关系】决定

QQ截图20221008185610.png

原因:

js事件传播方式(方向)有两种,分别是事件冒泡和事件捕获,两者传播方向相反

  • 事件冒泡:结构上【而非视觉上】是嵌套关系的元素,设置同一事件 自子元素冒泡向父元素(自底向上)
  • 事件捕获: 结构上【而非视觉上】是嵌套关系的元素,设置同一事件 自父元素捕获至子元素(自顶向下)
  • 设置方式: 通过绑定事件的API【addEventListener】,传入第三个参数来设置传播方式,默认值为false(事件冒泡),true(事件捕获)
        const divs = document.getElementsByTagName('div')
        divs[0].addEventListener('click', function () {
            console.log('father');
        },true)
        divs[1].addEventListener('click', function () {
            console.log('son');
        },true)
        divs[2].addEventListener('click', function () {
            console.log('sun');
        },true)

当我们将三个div都设置成事件捕获后,能看到最先触发事件的是father这个div QQ截图20221008185610.png

我们能够发现,传播方式不同,事件执行的顺序也会不同,当我们打破常规,不再将三个div进行统一设置传播方式,而让二种方式都存在时,事件的触发顺序又将如何? 将son这个div设置为捕获,其他两div设置为冒泡,代码如下:

        const divs = document.getElementsByTagName('div')
        divs[0].addEventListener('click', function () {
            console.log('father');
        },false)
        divs[1].addEventListener('click', function () {
            console.log('son');
        },true)
        divs[2].addEventListener('click', function () {
            console.log('sun');
        },false)

点击sun这个div:

QQ截图20221008192321.png

原因:

输出这样的打印结果是因为一个事件的触发,是由根节点html出发开始捕获,一直向内层寻找事件源,找到之后执行事件然后再回溯(冒泡)回根节点,因此事件触发有三个阶段:捕获阶段=>目标阶段=>冒泡阶段,先后顺序显而易见是捕获在先,冒泡在后。

阻止事件传播:

在不同的业务需求下,我们往往需要阻止非必要的事件传播,只希望当前元素才能触发事件。 事件对象概念:给事件处理函数传入一个参数,系统会事件对象赋给该参数,事件对象中包含事件的各种信息以及一些API,其中就有阻止事件传播的API 方法: 通过给事件处理器传入事件对象,调用事件对象的stopPropagation函数即可

        divs[1].addEventListener('click', function (e) {
            console.log('son');
            e.stopPropagation()
        },true)