Skip to content

事件委托的优点

  • 将事件绑定到目标元素的父级或者更上一级,不需要给所有的元素都绑定事件,减少内存占用空间,提升性能。
  • 动态新增的元素无需重新绑定事件

遇到的问题

网络上很多讲解事件委托的例子都是举的:

HTML
<ul id="myLink">
  <li id="1">aaa</li>
  <li id="2">bbb</li>
  <li id="3">ccc</li>
</ul>
<ul id="myLink">
  <li id="1">aaa</li>
  <li id="2">bbb</li>
  <li id="3">ccc</li>
</ul>

这样的例子复杂度不够,不太能覆盖到实际场景,往往实际中,v-for 循环出一个个 item,而 item 内部有很多个元素,或者 item 内元素还存在嵌套关系,当发生点击事件时,捕捉到的 event.target 不一定是我们需要执行操作的目标元素。举个例子:

HTML
<ul id="myLink">
    <li id="1">
        <a>
            <img src="xxx" />
            <span>查看大图</span>
            <span>评论</span>
        </a>
    </li>
    <li id="2">
        <a>
            <img src="yyy" />
            <span>查看大图</span>
            <span>评论</span>
        </a>
    </li>
    <li id="3">
        <a>
            <img src="zzz" />
            <span>查看大图</span>
            <span>评论</span>
        </a>
    </li>
</ul>
<ul id="myLink">
    <li id="1">
        <a>
            <img src="xxx" />
            <span>查看大图</span>
            <span>评论</span>
        </a>
    </li>
    <li id="2">
        <a>
            <img src="yyy" />
            <span>查看大图</span>
            <span>评论</span>
        </a>
    </li>
    <li id="3">
        <a>
            <img src="zzz" />
            <span>查看大图</span>
            <span>评论</span>
        </a>
    </li>
</ul>

我们将事件委托到外层元素ul上,得到event.target,“查看大图”和“评论”的事件回调不相同,而event.target捕获的可能是 a 标签,可能是 img 标签,可能是 span 标签,那么我们如何判断点击的元素是否是目标元素呢?

方案

可以给目标元素上添加一个标识,如果event.target上有这个标识,则得到目标元素,触发相应回调,否则就往父级元素上寻找是否有目标标识,直到event.target === event.currentTarget停止查找。

这个唯一标识可以是类名,可以是自定义属性。

我遇到一个场景,在点击非热点区域要调用一个接口,这个接口需要这个列表项的 id,我是直接定义了一个自定义属性,值为 id,当事件捕捉到的元素上有这个自定义属性时,就读取 id,发送请求。

HTML
<ul @click="handleClick">
    <li v-for="(item,index) in dataList" :key=item.id id="item.id">
        <a>
            <img :src="item.img" />
            <span :data-image-view="item.id">查看大图</span>
            <span data-comment>评论</span>
        </a>
    </li>
</ul>
<ul @click="handleClick">
    <li v-for="(item,index) in dataList" :key=item.id id="item.id">
        <a>
            <img :src="item.img" />
            <span :data-image-view="item.id">查看大图</span>
            <span data-comment>评论</span>
        </a>
    </li>
</ul>
js
const handleClick = (e) => {
    let element = e.target;
    while (!element.dataset.imageView) {
        // 如果当前节点没有目标属性,则向父级寻找
        element = element.parentNode;
        if (element === e.currentTarget) {
            // 找到最外层了,直接退出
            return;
        }
    }
    // 找到目标元素了,获取值,然后调接口
    jumpToDetail(element.dataset.imageView);
};
const handleClick = (e) => {
    let element = e.target;
    while (!element.dataset.imageView) {
        // 如果当前节点没有目标属性,则向父级寻找
        element = element.parentNode;
        if (element === e.currentTarget) {
            // 找到最外层了,直接退出
            return;
        }
    }
    // 找到目标元素了,获取值,然后调接口
    jumpToDetail(element.dataset.imageView);
};