一篇文章让你彻底了解 事件捕获、事件冒泡、事件委托

也许你在学习前端知识或者面试题的时候,有了解到一个问题叫做 事件委托,或许你也可能背了一些死的定理,但你有真正了解过这个事件委托 该怎么做,什么时候做吗?

在学习 事件委托 之前需要先学习一下 事件捕获 和 事件冒泡

捕获 和 冒泡 是指 DOM树 处理事件的两种不同的方式,简单理解可以视为两个不同阶段

事件捕获

当某个地方的时候,会从 DOM树 的根节点出发直到到达你点击的地方,就好像警察知道了犯人在哪个地方,可以迅速出动去抓捕,这个过程叫做捕获

事件冒泡

当捕获阶段结束后,会从捕获的目标处向 DOM树 扩散,就好比锅里的沸腾的水从底部一直向高处咕噜咕噜冒泡,这个阶段就是冒泡阶段

看到这里,你应该已经明白了这两者的先后顺序和特点,是的,捕获阶段 先于 冒泡阶段

思考一下,下面的代码的先后输出是什么

<div id="parent">
    <div id="child">
      <button id="btn">Click me</button>
    </div>
  </div>
  <script>
    const parent = document.getElementById('parent');
    const child = document.getElementById('child');
    const btn = document.getElementById('btn');

    // 在事件捕获阶段触发事件处理程序
    parent.addEventListener('click', function() {
      console.log('Parent');
    }, true);

    child.addEventListener('click', function() {
      console.log('Child');
    }, true);

    btn.addEventListener('click', function() {
      console.log('Button');
    });

  </script>

输出顺序:Parent Child Button

刚好符合 捕获阶段 的先后顺序,点击按钮的时候,先是从 根节点(parent)到子节点(child)再到子节点的子节点(button)

值得注意的是,我们给 事件监听器(addEventListener)的第三个参数(事件传播阶段)设置了 true,当为 true 的时候,启动的事件捕获阶段

相反,设为false也是默认状态的时候,就是冒泡阶段,这个时候的输出顺序就完全不一样了,当为冒泡阶段的时候

输出顺序:Button Child Parent

如果你想捕获和冒泡的时候都各自触发一次,也可以,但是需要再绑定一个事件

  // 在事件捕获阶段触发事件处理程序
  parent.addEventListener('click', function() {
    console.log('Parent capture');
  }, true);

  // 在事件冒泡阶段触发事件处理程序
  parent.addEventListener('click', function() {
    console.log('Parent pop');
  }, false);

事件委托

你一定会说,事件委托就是把绑定在 子级元素 的事件绑定在 父级元素 里面,比如把绑定在 li 标签的事件绑定在 ul 上,这样就可以提高代码性能和避免添加过多的事件处理

你说的很对,请你用代码演示一下具体怎么写呢,如果我是面试官我就会这样问你

可能此时的你瞬间就慌了:完了我只会说不会写呀 

一个班级里面有很多学生,但是只有一个老师,老师要让同学上黑板写字,如果给每个同学都发一根粉笔就会造成大量的开销,特别是当学生特别多的时候,但是每次上黑板来写只有一个学生,所以可以让老师拥有粉笔,当点到对应的同学的时候就把粉笔给他让他写,这样就只需要一个粉笔就能同样完成需求或任务

在这里以Vue2为例

<template>
  <div>
    <ul @click="handleGetItem">
      <li class="item" v-for="item in list" :key="item.id" :data-id="item.id">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      list: [
        { id: 1, name: 'Asuka' },
        { id: 2, name: 'Nikaido' },
        { id: 3, name: 'Nishino' },
        { id: 4, name: 'Shizuka' },
      ]
    }
  },
  methods: {
    // 获取具体点击的是谁
    handleGetItem(e) {
      const target = e.target;
      // 确保你点击的是 类名为 item 的标签
      if (target.closest('.item')) {
        // dataset是一个对象,它包含的信息主要是你在标签上设置的标识,比如 :data-id
        // data- 是固定的,id可以是其他的 wd,dd 之类的
        const itemID = target.closest('.item').dataset.id;
        this.handleSomething(itemID);
      }
    },
    // 拿到它的信息去做你真正想处理的事情
    handleSomething(itemID) {
      console.log(itemID);
    }
  }
}
</script>

如果要用事件委托,就一定要在每一个 子级元素 绑定一个标识,这里不是指 key,而是 js 可以拿到的 :data-xx

它会把 :data-xx 的 xx 作为一个对象的 键值对 的键,值自然就是你绑定的值,item的id

当我们点击每一个 item 的时候,事件会冒泡,冒泡到父级元素,然后父级元素可以根据你得到你点击的 item 的标识去获取到你具体点的是哪个 item,这样也是拿到了你需要操作的元素了

如果你认真看完了这篇文章,相信你对事件捕获、事件冒泡、事件委托都有了更深入跟深刻的了解,当下次面试的时候,还可以稍微操作一手让面试官惊讶一下

最后,还是希望对你有帮助,下次再见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值