事件流、事件冒泡、事件捕捉、事件委托

事件流

当我们触发一个dom事件(e.g: click),都会进行一次事件对象传播的过程,传播节点包括事件源和它上面的所有祖先节点,用于告知这些节点一次事件的发生。

发送事件对象之前,需要先确定此次的传播路径,这是一个有序的dom节点的列表,列表最后一个节点是事件源,列表的前面都是事件源的祖先节点。

确定路径后,一次传播会经过三个阶段:捕获阶段、目标阶段、冒泡阶段。如果某个节点不支持某个阶段,或者传播已结束都不会触发相应的事件监听函数。

可参考下面的图片:
在这里插入图片描述

  • 捕获阶段:事件对象从window传播到事件源的父级节点。
  • 目标阶段:事件对象到达事件对象的事件源。
  • 冒泡阶段:和捕获阶段相反,事件对象从父级节点传播到window。

如果你在事件传播中的某个节点调用了evt.stopPropagation,则会阻止后续的传播过程。

概念

我们常讲的事件冒泡和事件捕捉就是上面的捕获阶段和冒泡阶段。

事件委托则是利用了事件冒泡的原理控制多个元素的事件处理,在下方通过实例讲解。

既然我们知道了事件传播的三个阶段,那每个dom元素的事件触发应该在那个阶段呢?这是在你注册事件的时候决定的,通过addEventListener方法的第三个参数useCapture,默认为false,则在冒泡阶段触发事件,设置为true则在捕获阶段触发,但是对于事件源(触发事件的元素)来说,他只存在于目标阶段,所以不管你将useCapture设置为true还是false,都是会触发事件函数的。

我们可以通过下面的实例来验证下。

实例讲解

事件冒泡
<div id="parent">
    <div id="child" class="child">点击</div>
</div>

<script>
    const parent = document.querySelector('#parent')
    const child = document.querySelector('#child')

    parent.addEventListener('click', function (e) {
        console.log('parent事件被触发,', this.id)
    }, false)

    child.addEventListener('click', function (e) {
        console.log('child事件被触发,', this.id)
    }, false)
</script>

上面parent元素设置了冒泡阶段进行事件处理,点击child元素,打印结果:

child事件被触发, child
parent事件被触发, parent

child元素的事件处理先触发。即使你将child的useCapture改为true,也是同样的结果,因为child元素是事件源,它只在目标阶段处理事件。

事件捕捉
<div id="parent">
    <div id="child" class="child">点击</div>
</div>

<script>
    const parent = document.querySelector('#parent')
    const child = document.querySelector('#child')

    parent.addEventListener('click', function (e) {
        console.log('parent事件被触发,', this.id)
    }, true)

    child.addEventListener('click', function (e) {
        console.log('child事件被触发,', this.id)
    })
</script>

parent元素设置了捕获阶段触发,打印结果:

parent事件被触发, parent
child事件被触发, child

parent元素的事件处理先触发。没问题。

事件委托

当有多个类似的元素需要绑定事件时,一个一个去绑定即浪费时间,又不利于性能,这时候就可以用到事件委托,给他们的一个共同父级元素添加一个事件函数去处理他们所有的事件,然后根据evt.target找到最终的事件源

<ul id="1ist">
    <li>111</li>
    <li>222</li>
    <li>333</li>
    <li>444</li>
</ul>

<script type="text/javascript">
    document.getElementById('1ist').addEventListener('click', function (e) {
        e.target.innerHTML = "被点击";
    });
</script>

来分析下,事件源为某一个li元素,点击时先经过捕获阶段:window -> ... -> ul,ul元素默认设置了冒泡阶段进行事件处理,所以什么都不发生。

再来到目标阶段,事件源li元素同样没设置事件处理。

最后到了冒泡阶段,ul -> ... -> window,刚好ul元素设置了冒泡阶段的事件处理函数,触发函数,然后通过事件对象e的target属性找到事件源是某个li元素,给他设置innerHTML。

  • e.target表示在事件传播过程中触发事件的源元素,在IE中是e.srcElement
  • 并且e.target有很多属性可以操作
  • event.target.nodeName   // 获取事件触发元素的标签name(li,p…)
  • event.target.id      // 获取事件触发元素id
  • event.target.className  // 获取事件触发元素classname
  • event.target.innerHTML  // 获取事件触发元素的内容(li)

最后展示下e.stopPropagation()的兼容写法(兼容IE)

function stopPropagation(e) {  
    if (e.stopPropagation) {  
        e.stopPropagation();  
    } else {  
        window.event.cancelBubble = true;  
    }  
}  
  • 10
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值