addeventlistener事件第三个参数_JavaScript 中那些不会冒泡的事件

事件的捕获和冒泡

DOM 事件模型包括捕获和冒泡,捕获是从上往下到达目标元素,冒泡是从当前元素,也就是目标元素往上到 window。

如下图所示, DOM事件(专指 DOM2 级事件)包含以下三个阶段

  1. 事件捕获阶段
  2. 处于目标阶段
  3. 事件冒泡阶段

v2-a7cd95ba10096960dcb94d13998065f8_b.jpg

第二个阶段其实可以归属于冒泡阶段,这里先这么分。并不是所有浏览器的事件体系都遵循这三个阶段,比如一些旧版本的 IE 并不支持事件捕获。相对来说,事件冒泡的兼容性更好些,基本所有现代浏览器都支持事件冒泡。

由于事件可以冒泡,那么事件就可以放在最上层的元素中处理(React 就是放在 document 中处理),这个过程叫做事件委托,事件委托是我们编写 js 代码常用到的手段,但是有些事件比较特殊,常常导致事件委托失效,产生一些千奇百怪的问题。这些问题的实质是由于有些事件没有进行冒泡导致的,我们今天就来看看这些常用却又特殊的事件。

注意,以下的所有示例代码运行在 chrome 浏览器,版本是 83。

scroll

scroll 事件在元素滚动条在滚动时触发。我们假设有如下 html 结构.

<

最外层元素的 id 是 container。内部的第一个元素 id 是 outer,outer 的高度要高于 caontainer。outer 内部的元素 id 是 inner,inner 的高度高于 outer。

这样的话,outer 和 container 上就会存在滚动条,也就是这两个元素都是有 scroll 事件的。

v2-b42b5f08a47579d37ad3b77a8e92af51_b.jpg

我们通过如下代码为 container 和 outer 添加 scroll 监听事件。

document

注意,这里 addEventListener 的第三个参数是 false,也就是 useCapture 为 false。如果 scroll 事件可以冒泡的话,当触发 outer scroll 的时候,事件就会冒泡到 container,触发 container scroll。

当我们在 inner 区域滚动,实际的效果是。

outer 

outer 滚动的时候事件并没有冒泡到 container 区域。这说明了 scroll 事件是不会冒泡的。

现在我们换一种方式添加监控。

document

此时的 useCapture 为 true,同样是在 inner 上向下滚动。控制台显示如下:

container 

这符合我们对事件捕获的预期,scroll 事件捕获的方向从 container 到 outer,依次触发回调方法。

scroll 事件的特殊性除了不会进行冒泡这一点外,还有一点,那就是 scroll 事件的无法取消的,使用 stopPropagation 或者是 preventDefault 都无法阻止 scroll 事件,这也很好理解,scroll 连冒泡都没有了,阻止冒泡传播当然也含无意义。。

e

那如何取消 scroll 事件呢,其实这个问题就是个伪命题,因为先有滚动才有滚动事件。想要阻止滚动,必须在事件发生之前就阻止,我们一般的做法是阻止 wheel 和 touchstart 的默认动作。

document

此时用滚轮操作事件就会失效,outer 区域无法滚动,当然,这种方法并不能禁用滚动条,我们还需要禁用滚动条的拖动事件。

上述的方法是不是很麻烦,所以这种方法我们一般用不到,我们有更常用也更简单的方式,直接修改样式。

overflow:hidden

scroll 事件总结

  1. scroll 事件不会冒泡,这个带来的影响就是,当我们去做事件委托的时候,其它的大部分事件可以在冒泡阶段的时候完成委托,而 scroll 事件必须在捕获阶段完成委托。
  2. scroll 事件无法取消( 没有冒泡的基本都没法取消 ),scroll 回调中的 preventDefault 和 stopPropagation 都是无效的。

blur & focus

和 scroll 事件一样,focus 和 blue 事件也是无法冒泡,无法取消的。我们为如下文档添加 focus 事件。

<

点击 input,显然,由于 focus 事件没有冒泡。控制台显示 input focus。

input focus

变更参数 useCapture 为 true,点击 input,此时控制台显示 container 和 input 的回调方法 。

document

提到 blur/focus,就会想到和它们很像的两个事件,那就是 focusout/focusin。它们和前者的主要区别就是 focusout/focusin 事件会冒泡。如果同时存在的话,focus 先于 focusin。blur 先于 focusout。

比较可惜的是,focusout/focusin 存在兼容性问题,例如 fireFox 低版本不支持这两个事件。当然,如果我们使用高版本浏览器的话,完全可以替代 blur/focus。

v2-d79fa1420fa9dbf7c483edd3057ef76e_b.jpg

focus/blur 做事件委托的时候需要注意,在捕获阶段进行监听。否则会导致事件失效。

Media 事件

由媒介(比如视频、图像和音频)触发的事件,都不冒泡。以下列出部分事件。

...
onpause 当媒介被用户或程序暂停时运行的脚本
onplay  当媒介已就绪可以开始播放时运行的脚本
onplaying 当媒介已开始播放时运行的脚本
onsuspend 在媒介数据完全加载之前不论何种原因终止取回媒介数据时运行
...

假设我们有如下 audio 标签。

<

我们需要监听音频开始播放的事件。由于所有的由媒体触发的事件都不冒泡,所以我们只能在捕获阶段进行事件委托。

document

这样在上级元素中就能知道 audio 开始播放了。

container play 
audio play

mouseleave & mouseenter

mouseleave 和 mouseenter 事件同样不会冒泡,当这种不冒泡的特性发生在鼠标事件的时候,显得额外的符合直觉。我们看下例子。

<

形状大概就是如下这个样子,从外到内越来越小。

_ 

我们绑定 mouseleave 事件,然后 我鼠标从 inner 滑动到 outer。

document

结果只触发了 inner mouseleave 事件,是不是很符合直觉。

与 mouseleave/mouseenter 事件非常相似的事件是 mouseout/mouseover,它们的区别就是 mouseout/mouseover 会触发冒泡,还是这个例子,我们将 mouseleave 换成 mouseout。

document

然后 鼠标从 inner 滑动到 outer。结果三个事件都触发了。

inner 

在实际写代码的过程中,我们使用 mouseout 和 mouseover 会经常导致一些很奇怪的问题,遇到的时候,希望你能想起来,是事件冒泡搞得鬼。

JS 事件体系的原理比较容易理解,但是细节会比较繁琐一些,比如本文中罗列的这些事件,它们在做事件委托的时候就是坑,一不注意就会导致事件委托失效。

React 的事件体系就是基于事件委托建立的,其中有很大的部分都是处理不同事件之间的差异性的,对于这些不会冒泡的事件,React 也进行了处理。如果想了解 React 事件体系相关内容,请阅读我的 React 源码系列中的事件章节,不需要看 React 其余章节也能很容易的看懂。

如果您觉得有所收获,麻烦点个赞吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值