js 事件委托,如何对嵌套的对象进行事件绑定?

2020-08-03 10:11:08 +08:00
 mostkia

起因:客户需要匹配一个量比较大的列表,并且随时都会更新内容,点击还得绑定事件,所以理所当然的使用了 js 的事件托管,但实践中却发现了问题,以下是代码:

html 代码

<body>
<ul>
    <li class="test">
        <img src="测试图片.jpg">
        <p>测试文字</p>
    </li>
    <li class="test">
        <img src="测试图片.jpg">
        <p>测试文字</p>
    </li>
    <li class="test">
        <img src="测试图片.jpg">
        <p>测试文字</p>
    </li>
</ul>
</body>

js 代码

var body = document.querySelector('body');
body.addEventListener('click',function(event){
    var event = event || window.event;
    var tisTag = event.srcElement || event.target;
    if(tisTag.className == 'test'){
   	alert('li 被点击了!');
    }
});

出现的问题: 点击 li 列表后,激活的对象是 p 标签和 img 标签,导致预先编写的针对 li 标签的代码没有反应。有没有办法忽略子元素 p 和 img ?

3459 次点击
所在节点    JavaScript
22 条回复
zhzbql
2020-08-03 10:32:30 +08:00
var tisTag = event. currentTarget
vivipure
2020-08-03 10:35:35 +08:00
首先 img 和 p 充满了 li 的空间,e.target 不可能指向 li 。忽略子元素,加个 pointer-events: none;就可以了,但是没法触发 li

建议你获取 target 时做判断,如果是 img 和 p,就获取它的父节点 。
ChanKc
2020-08-03 10:50:58 +08:00
iwasthere
2020-08-03 10:51:14 +08:00
while(!tisTag. className == 'test'){
if(tisTag. className == 'test') break;
tisTag = tisTag.parentNode
}
大致写了下,应该就这个意思吧
mostkia
2020-08-03 10:53:46 +08:00
@vivipure 好吧,看来没有更优雅的方案来做到了,可能得额外做额外的判断了,但这个方法太笨了,因为 li 内存在的东西越多,要写的判断也就越复杂,上面的代码只是示范,实际肯定还有更多嵌套之类的,看来每一种方法都不是全能的。
otakustay
2020-08-03 10:59:41 +08:00
1 楼的 currentTarget 能解决问题的吧,或者 e.target.matches('.test *')
sixway
2020-08-03 11:03:30 +08:00
太多嵌套的需要判断。
ChanKc
2020-08-03 11:04:17 +08:00
#3 event.target.closest('ul > li.test')
mostkia
2020-08-03 11:09:04 +08:00
@otakustay @iwasthere @zhzbql @ChanKc @vivipure 好的,感谢诸位出谋划策,具体我一个一个测试,已发送感谢~
gdrk
2020-08-03 11:09:18 +08:00
1 楼的方案就可以啊
vcfvct
2020-08-03 11:13:22 +08:00
event 要 bubble up 上去 target 总是点击的那个最深 element, 所以还是应该得往上找 parent 才行吧, 不一定非得绑定在 body 上, 在 最 close 的`ul`上可能能让范围小一些.
```
var body = document.querySelector('body');
body.addEventListener('click', function (event) {
var event = event || window.event;
let tisTag = event.target;
while (tisTag && tisTag.tagName !== 'LI') {
tisTag = tisTag.parentNode
if (tisTag === body) {
tisTag = null
break;
}
}
if (tisTag && tisTag.className === 'test') {
alert('li 被点击了!');
}
```
netnr
2020-08-03 11:19:47 +08:00
var body = document.querySelector('body'),
lis = document.getElementsByTagName('li');

body.addEventListener('click', function (event) {
var event = event || window.event;
var tisTag = event.srcElement || event.target;
for (var i = 0; i < lis.length; i++) {
var li = lis[i];
if (li.contains(tisTag)) {
alert('li 被点击了!');
}
}
});
mostkia
2020-08-03 11:21:34 +08:00
@vcfvct 其实就是想一揽子让 body 监视所有页面行为,后期好维护一些,感觉页面无关内容不多时应该没什么问题,看来的确得准备额外的代码来进行判断具体点击了什么东西
mostkia
2020-08-03 11:24:04 +08:00
@netnr @vcfvct 哈哈,好的,结果还是挺麻烦的,之前感觉事件托管是全能的,结果的确还是明确应用范围才更好一些。
will0404
2020-08-03 11:27:52 +08:00
一般来说事件绑在 ul 上比较好,用 body 的话页面上其它元素也会触发一次无意义的判断。
otakustay
2020-08-03 12:49:11 +08:00
其实乖乖用 jQuery 就好了,自己处理这些多麻烦
flowfire
2020-08-03 17:02:23 +08:00
你绑定在 body 上。
事件触发在 p 或者 img 上
对于监听器来说,只有这两个元素( target 和 currentTarget )是特殊的,其他元素都是冒泡过程中的路径而已。
当然没有办法区别。不然他怎么知道你想区别对待 li 还是 ul ?
建议你换一种提问方式,不要问如果在冒泡到 li 的时候触发 function,直接把你要执行的方法代码和需求贴出来,看看能不能改变方法实现这个功能?
nonocris
2020-08-03 19:21:39 +08:00
@zhzbql @otakustay @gdrk currentTarget 不应该是始终指向绑定事件的元素吗,按楼主这个写法,currentTarget 始终都是 body 吧。是我基础不行记忆出现了偏差吗...
otakustay
2020-08-03 22:20:20 +08:00
@nonocris 是我记错了,currentTarget 没用
mostkia
2020-08-04 09:17:12 +08:00
@flowfire 嗯,目前已经解决了,既然没有讨巧的办法,那就特事特办,如果出现这类内含标签的节点,直接判断是否是起泡对象本身,不是的话,则通过递归函数使用 parentNode 检索父级节点,我为需要有事件的节点都准备了一个专有属性,递归函数会直到发现这个需要的专有属性为止,这样就不会被内含的乱七八糟的嵌套影响了,为了保证递归的准确性,不使用可能发生重复的标签名称或者 class 来检索 event 对象。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/695165

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX