大家用addEventListener肯定也听过removeAddeventListener,但是不少朋友使用有误区。

事件的添加和移除

// 示例1
// 添加事件
document.addEventListener('click', (e) => {
    console.log('冒泡 点击', e)
});

// 移除事件
document.removeEventListener('click', (e) => {
    console.log('冒泡 点击', e)
});

上面的添加没有问题,但是移除没有效果了,就算移除的方法的代码跟添加一模一样也不行。
因为在使用removeEventListener的时候,要确保回调函数时同一个

// 示例2
function fn(e) {
    console.log('冒泡 点击', e)
}
// 添加事件
document.addEventListener('click', fn);

// 移除事件
document.removeEventListener('click', fn);

向上面的方式使用就没有问题了。

但是我们经常在使用框架(以vue为例)的时候,有意无意的又会放上面示例1的错误了,我就经常看到这样的错误。

// 示例2
methods: {
    fn(e) {
        console.log('冒泡 点击', e);
        this.abcFn();
    },
    abcFn() {
        this.abc =[Date.now()];
    },
}
mounted() {
    // 添加事件
    document.addEventListener('click', this.fn.bind(this));

    // 移除事件
    document.removeEventListener('click', this.fn.bind(this));
}

这样写的童鞋主要是因为在fn方法在还要调用abcFn方法,怕this不对,所以想着bind以下,但是这样其实犯了跟示例1一样的问题,因为每次bind都会返回一个新方法的。

事件的冒泡与捕获

addEventListener其实是有三个参数的,就是是否使用事件捕获,默认是false,也就是默认用的是事件冒泡,这也是现在主流的方式。

// 示例4
<template>
  <section>
    <div id='abc' ref='abc'>
      abc
    </div>
    <section>
      <div id='efg' ref=efg>
        efg
        {{abc}}
      </div>
    </section>
  </section>
</template>
this.$refs.abc.addEventListener('click', (e) => {
      console.log('abc 冒泡 点击', e);
});
this.$refs.efg.addEventListener('click', (e) => {
    console.log('efg 捕获 点击', e);
}, true);
document.addEventListener('click', (e) => {
    console.log('document 冒泡 点击', e);
})

1.如果我们在id为abc的div内点击,我们会看到打印两个冒泡
file

2.如果在id为efg内点击,会先打印一个捕获,打印一次冒泡。
file

<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
     (adsbygoogle = window.adsbygoogle || []).push({
          google_ad_client: "ca-pub-3013839362871866",
          enable_page_level_ads: true
     });
</script>

3.如果我们在id为abc和id为efg的div外点击,只会打印一次冒泡
file
这三种情况应该不难理解,觉得不好理解的话,建议静下心来了解下时间的捕获和冒泡过程。

使用stopPropagation

我们有时需要阻止冒泡。
1.我们在abc里面点击的时候,不希望触发外层的 document的点击回调

this.$refs.abc.addEventListener('click', (e) => {
      console.log('abc 冒泡 点击', e);
      e.stopPropagation();
});

file

2.我们在efg里面点击的时候,不希望触发外层的 document的点击回调

this.$refs.efg.addEventListener('click', (e) => {
      console.log('efg 捕获 点击', e);
      e.stopPropagation();
}, false);

file

这里的一样跟我们的预期是一致的。

3.但是我们如果把上面绑在efg的事件捕获,改成绑在document上面呢

document.addEventListener('click', (e) => {
      console.log('efg 捕获 点击', e);
      e.stopPropagation();
}, false);

为什么这样说呢?因为一般使用过程中大家都习惯使用事件冒泡,但是有的第三方库偶尔会使用事件捕获,这时要注意了,因为现在一般都会把事件绑定到document上,如果在事件使用了阻止冒泡会怎样?

file
现在之后打印捕获了,即使在id为efg的div里面点击也是如此。也就是说,你在时间冒泡里面的写回调方法都没有执行了,是不是很坑。

总结

  1. 慎用时间捕获,尤其是在外层,比如document里面使用,
  2. 慎用阻止冒泡

发表评论

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据