起因:客户需要匹配一个量比较大的列表,并且随时都会更新内容,点击还得绑定事件,所以理所当然的使用了 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 ?
1
zhzbql 2020-08-03 10:32:30 +08:00 1
var tisTag = event. currentTarget
|
2
vivipure 2020-08-03 10:35:35 +08:00 1
首先 img 和 p 充满了 li 的空间,e.target 不可能指向 li 。忽略子元素,加个 pointer-events: none;就可以了,但是没法触发 li
建议你获取 target 时做判断,如果是 img 和 p,就获取它的父节点 。 |
4
iwasthere 2020-08-03 10:51:14 +08:00 1
while(!tisTag. className == 'test'){
if(tisTag. className == 'test') break; tisTag = tisTag.parentNode } 大致写了下,应该就这个意思吧 |
5
mostkia OP @vivipure 好吧,看来没有更优雅的方案来做到了,可能得额外做额外的判断了,但这个方法太笨了,因为 li 内存在的东西越多,要写的判断也就越复杂,上面的代码只是示范,实际肯定还有更多嵌套之类的,看来每一种方法都不是全能的。
|
6
otakustay 2020-08-03 10:59:41 +08:00 1
1 楼的 currentTarget 能解决问题的吧,或者 e.target.matches('.test *')
|
7
sixway 2020-08-03 11:03:30 +08:00
太多嵌套的需要判断。
|
8
ChanKc 2020-08-03 11:04:17 +08:00 1
#3 event.target.closest('ul > li.test')
|
9
mostkia OP |
10
gdrk 2020-08-03 11:09:18 +08:00
1 楼的方案就可以啊
|
11
vcfvct 2020-08-03 11:13:22 +08:00 1
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 被点击了!'); } ``` |
12
netnr 2020-08-03 11:19:47 +08:00 1
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 被点击了!'); } } }); |
13
mostkia OP @vcfvct 其实就是想一揽子让 body 监视所有页面行为,后期好维护一些,感觉页面无关内容不多时应该没什么问题,看来的确得准备额外的代码来进行判断具体点击了什么东西
|
14
mostkia OP |
15
will0404 2020-08-03 11:27:52 +08:00
一般来说事件绑在 ul 上比较好,用 body 的话页面上其它元素也会触发一次无意义的判断。
|
16
otakustay 2020-08-03 12:49:11 +08:00
其实乖乖用 jQuery 就好了,自己处理这些多麻烦
|
17
flowfire 2020-08-03 17:02:23 +08:00
你绑定在 body 上。
事件触发在 p 或者 img 上 对于监听器来说,只有这两个元素( target 和 currentTarget )是特殊的,其他元素都是冒泡过程中的路径而已。 当然没有办法区别。不然他怎么知道你想区别对待 li 还是 ul ? 建议你换一种提问方式,不要问如果在冒泡到 li 的时候触发 function,直接把你要执行的方法代码和需求贴出来,看看能不能改变方法实现这个功能? |
18
nonocris 2020-08-03 19:21:39 +08:00
|
20
mostkia OP @flowfire 嗯,目前已经解决了,既然没有讨巧的办法,那就特事特办,如果出现这类内含标签的节点,直接判断是否是起泡对象本身,不是的话,则通过递归函数使用 parentNode 检索父级节点,我为需要有事件的节点都准备了一个专有属性,递归函数会直到发现这个需要的专有属性为止,这样就不会被内含的乱七八糟的嵌套影响了,为了保证递归的准确性,不使用可能发生重复的标签名称或者 class 来检索 event 对象。
|