JavaScript事件的捕获阶段(Capture phase)

最近项目中遇到一个实际问题,自己搞了半天没解决,最后在StackOverflow上求助,不到3分钟就得到了解决方案,而且答案意外的很简单——加一个参数。

场景是这样的:有一个表格(事实上这样的表格在这个系统很多),每一行(row)是顾客的信息(名字,订单号,付款金额...),要求点击表格行(tr元素)打开订单详情,点击顾客名字(包裹在一个a元素里,a在td里)时打开顾客信息页面,不打开订单详情。这不简单直接么,jQuery把a元素和tr元素的事件都委托给tbody或者table不就可以了么?

$('tbody')
    .on('click', 'tr', function() {
        alert('order detail...')
    })
    .on('click', 'a', function(e) {
        e.stopPropagation()
        alert('contact detail')
    })   

如果只是在当前单个页面的代码里写这段,没问题。但因为项目很多页面都有这样包含"<a>顾客名字</a>"的地方,于是我干脆在一个每个页面都会引用的js文件中加了一段把这样的a元素(元素类名取为“gotocontact”)的handler委托给document元素的代码,如下(这里假设a元素是叶子元素,即不会有子元素):

// 因为此时还没引入jQuery,所以用了原生方法
document.addEventListener('click', function(e) {
    var el = e.srcElement || e.target;

    if (el && el.classList && el.classList.contains('gotocontact')) {
        e.stopPropagation();
        alert('going to contact...')
    }
})

然后在那个页面的代码里把tr元素的handler委托给tbody(id为“list”):

$('#list')
    .on('click', 'tr', function() {
        alert('order detail...')
    })

结果这样是有问题的,当点击a元素时两个handler都会触发,不符合预期。

stackoverflow上的答案是在addEventListener参数里添加第三个参数:

document.addEventListener('click', function(e) {
    var el = e.srcElement || e.target;

    if (el && el.classList && el.classList.contains('gotocontact')) {
        e.stopPropagation();
        alert('going to contact...')
    }
}, true)
// ^^^^ add third parameter

 问题解决。一个参数就解决了?!

知道addEventListener有这个参数选项(MDN),但是根本不知道起什么作用。

然后开始搜索,看到了这篇文章,算是讲得比较通俗易懂的:

W3C标准中DOM事件有3个阶段:capture phase(捕获阶段), target phase(目标阶段), bubble phase(冒泡阶段)。而常用的on<event> 和 addEventListener(event, handler) (注意没有第三个参数)只会作用于后两个阶段。如下图(来自W3C)

W3C DOM event flow

把W3C对这张图的说明简单翻译下搬过来:

事件生成后,事件的传播路径(propagation path)先被确定下来,路径是一个有序列表,列表中最后一个元素是目标元素,往前依次是目标元素的父元素、祖先元素,一直到Window对象。接下来事件开始传播(propagate):

1.捕获阶段。事件对象从Window对象开始沿传播路径向下,依次经过各元素传播至目标元素的父元素;

2.目标阶段。事件对象到达目标元素;

3.冒泡阶段。事件对象从目标元素的父元素开始沿传播路径向上,依次经过各元素传播至Window对象。

 

照着图拿本例来看。没加第三个参数时,两个handler都只在冒泡阶段起作用(jQuery的on方法就是addEventListener不加第三个参数的包装)。当我点击a.gotocontact时,事件对象从Window到达a标签元素这段时间内都没有触发handler。事件对象到达tbody#list时,触发其handler,alert('order detail...'),而此处没有stopPropagation(也不能有,否则事件传播不到document,document的handler就不会触发)。之后到达document,触发handler,alert('going to contact'),而此处的stopPropagation也只是不让事件传到最后的Window对象,事件基本走完了传播路径的一个往返。

加上第三个参数时,addEventListener添加的handler会在捕获阶段起作用。事件传播到document元素时触发handler,这里的stopPropagation停止事件进一步传播。事件刚走了一步就被停止传播,到不了tbody#list,达到预期效果。

 

 

 参考:

1. Bubbling and capturing

2. UI Events

3. Event | MDN

转载于:https://www.cnblogs.com/Monkey-D-Pixel/p/10070331.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值