Appearance
事件委托的优点
- 将事件绑定到目标元素的父级或者更上一级,不需要给所有的元素都绑定事件,减少内存占用空间,提升性能。
- 动态新增的元素无需重新绑定事件
遇到的问题
网络上很多讲解事件委托的例子都是举的:
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);
};