
起因:客户需要匹配一个量比较大的列表,并且随时都会更新内容,点击还得绑定事件,所以理所当然的使用了 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 var tisTag = event. currentTarget |
2 vivipure 2020-08-03 10:35:35 +08:00 首先 img 和 p 充满了 li 的空间,e.target 不可能指向 li 。忽略子元素,加个 pointer-events: none;就可以了,但是没法触发 li 建议你获取 target 时做判断,如果是 img 和 p,就获取它的父节点 。 |
4 iwasthere 2020-08-03 10:51:14 +08:00 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 楼的 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 #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 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 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 事件触发在 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 对象。 |