我不知道的事件捕获

561 阅读3分钟

之所以有这篇文章,来源一个需求

页面存在多个视频,只有一个单独的背景音乐,当视频播放时,音乐要暂停,视频暂停时,音乐要恢复

普通实现

// 视频1
video1.addEventListeners('play',() => {
    // 暂停背景音乐
})

video1.addEventListener('pause', () => {
    // 播放背景音乐
})
// 视频2
video2.addEventListeners('play',() => {
  // 暂停背景音乐
})

video2.addEventListener('pause', () => {
    // 播放背景音乐
})

一般的思路就是对每个视频元素都进行类似的处理,视频播放时,暂停音乐,视频暂停时,播放音乐,在这个场景上看起来还好,但是,如果视频是由ue那种富文本生成的,我们不好去拿到视频标签的id,或者不好去为视频绑定事件,那么该怎么办呢

事件委托??

事件委托是一种很好的技术,个人认为,它的长处不是说节省了事件绑定的内存其优势在于,它将子元素的事件移动到了父节点上管理,可以无需根据子元素的生命周期去维护事件,这一点才是更值得关注的

事件冒泡

我们常常想到的事件委托,那么就是利用事件冒泡实现,但是,video标签的playpause事件是不冒泡的,并不是所有的事件都冒泡,显而易见,我们使用事件委托的计划泡汤了

事件捕获

说到事件模型,常常大家都会提及到冒泡和捕获,但实际上,捕获很少被关注到,但是所有事件都会触发捕获,这就是捕获独一无二的价值,那么,我们的需求可以改写成

document.addEventListeners('play',() => {
    // 暂停背景音乐
}, true)

document.addEventListener('pause', () => {
    // 播放背景音乐
},true)

上述代码利用了捕获模型来实现事件委托,以一个类似中介者的设计模式实现了管理背景音乐和视频的播放管理,代码的维护性和扩展性都非常好

阻止捕获

我们常常看到阻止冒泡的代码是这样的

// 阻止事件冒泡
e.stopPropagation();

其实你把这个方法翻译一下

我以前下意思认为这个方法就是阻止冒泡

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<style>
  div {
    height: 300px;
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
  }
</style>
<body>
  <div id="p">

  </div>
</body>
<script>
  document.querySelector('body').addEventListener('click',(e) => {
    console.log("click body")
  },true)
  document.getElementById('p').addEventListener('click',() => {
    console.log("click p")
  },true)
</script>
</html>

上述例子是这样的

尝试加上e.stopPropagation()

document.querySelector('body').addEventListener('click',(e) => {
console.log("click body")
},true)

现在的结果是

就像这个方法的名字一样,它既可以阻止冒泡,也可以阻止捕获,它是用于阻止事件传播的,由于事件捕获是由document传递到目标元素,可以在目标元素的事件触发之前做一些处理,优化性能

小结

事件捕获是一个常常被忽略的模型,但是所有的事件都会捕获,一定冒泡,而且在捕获阶段,我们可以在目标元素触发之前,对事件做一些优化等等,大家兼容的兼容性问题,我在can i use上查看过,并且已经在实际项目中使用,兼容这一块没有问题,放心使用,在冒泡使用不了的时候,别忘了捕获。