js 事件委托,如何对嵌套的对象进行事件绑定? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐关注
Meteor
JSLint - a Javascript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
Javascript 权威指南第 5 版
Closure: The Definitive Guide
mostkia
V2EX    Javascript

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

  •  
  •   mostkia 2020-08-03 10:11:08 +08:00 4008 次点击
    这是一个创建于 1948 天前的主题,其中的信息可能已经有所发展或是发生改变。

    起因:客户需要匹配一个量比较大的列表,并且随时都会更新内容,点击还得绑定事件,所以理所当然的使用了 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 ?

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

    建议你获取 target 时做判断,如果是 img 和 p,就获取它的父节点 。
    iwasthere
        4
    iwasthere  
       2020-08-03 10:51:14 +08:00   1
    while(!tisTag. className == 'test'){
    if(tisTag. className == 'test') break;
    tisTag = tisTag.parentNode
    }
    大致写了下,应该就这个意思吧
    mostkia
        5
    mostkia  
    OP
       2020-08-03 10:53:46 +08:00
    @vivipure 好吧,看来没有更优雅的方案来做到了,可能得额外做额外的判断了,但这个方法太笨了,因为 li 内存在的东西越多,要写的判断也就越复杂,上面的代码只是示范,实际肯定还有更多嵌套之类的,看来每一种方法都不是全能的。
    otakustay
        6
    otakustay  
       2020-08-03 10:59:41 +08:00   1
    1 楼的 currentTarget 能解决问题的吧,或者 e.target.matches('.test *')
    sixway
        7
    sixway  
       2020-08-03 11:03:30 +08:00
    太多嵌套的需要判断。
    ChanKc
        8
    ChanKc  
       2020-08-03 11:04:17 +08:00   1
    #3 event.target.closest('ul > li.test')
    mostkia
        9
    mostkia  
    OP
       2020-08-03 11:09:04 +08:00
    @otakustay @iwasthere @zhzbql @ChanKc @vivipure 好的,感谢诸位出谋划策,具体我一个一个测试,已发送感谢~
    gdrk
        10
    gdrk  
       2020-08-03 11:09:18 +08:00
    1 楼的方案就可以啊
    vcfvct
        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 被点击了!');
    }
    ```
    netnr
        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 被点击了!');
    }
    }
    });
    mostkia
        13
    mostkia  
    OP
       2020-08-03 11:21:34 +08:00
    @vcfvct 其实就是想一揽子让 body 监视所有页面行为,后期好维护一些,感觉页面无关内容不多时应该没什么问题,看来的确得准备额外的代码来进行判断具体点击了什么东西
    mostkia
        14
    mostkia  
    OP
       2020-08-03 11:24:04 +08:00
    @netnr @vcfvct 哈哈,好的,结果还是挺麻烦的,之前感觉事件托管是全能的,结果的确还是明确应用范围才更好一些。
    will0404
        15
    will0404  
       2020-08-03 11:27:52 +08:00
    一般来说事件绑在 ul 上比较好,用 body 的话页面上其它元素也会触发一次无意义的判断。
    otakustay
        16
    otakustay  
       2020-08-03 12:49:11 +08:00
    其实乖乖用 jQuery 就好了,自己处理这些多麻烦
    flowfire
        17
    flowfire  
       2020-08-03 17:02:23 +08:00
    你绑定在 body 上。
    事件触发在 p 或者 img 上
    对于监听器来说,只有这两个元素( target 和 currentTarget )是特殊的,其他元素都是冒泡过程中的路径而已。
    当然没有办法区别。不然他怎么知道你想区别对待 li 还是 ul ?
    建议你换一种提问方式,不要问如果在冒泡到 li 的时候触发 function,直接把你要执行的方法代码和需求贴出来,看看能不能改变方法实现这个功能?
    nonocris
        18
    nonocris  
       2020-08-03 19:21:39 +08:00
    @zhzbql @otakustay @gdrk currentTarget 不应该是始终指向绑定事件的元素吗,按楼主这个写法,currentTarget 始终都是 body 吧。是我基础不行记忆出现了偏差吗...
    otakustay
        19
    otakustay  
       2020-08-03 22:20:20 +08:00 via iPad
    @nonocris 是我记错了,currentTarget 没用
    mostkia
        20
    mostkia  
    OP
       2020-08-04 09:17:12 +08:00
    @flowfire 嗯,目前已经解决了,既然没有讨巧的办法,那就特事特办,如果出现这类内含标签的节点,直接判断是否是起泡对象本身,不是的话,则通过递归函数使用 parentNode 检索父级节点,我为需要有事件的节点都准备了一个专有属性,递归函数会直到发现这个需要的专有属性为止,这样就不会被内含的乱七八糟的嵌套影响了,为了保证递归的准确性,不使用可能发生重复的标签名称或者 class 来检索 event 对象。
    zhzbql
        21
    zhzbql  
       2020-08-04 11:29:31 +08:00
    @nonocris 你的理解没错,是我忽略了事件处理已经委托到 body 了,当成事件绑定到 li 标签处理了, 所以解决方案还是要判断事件直接触发者是否为 li 的子孙元素
    mostkia
        22
    mostkia  
    OP
       2020-08-04 15:30:30 +08:00
    @zhzbql 如果为 li 节点部署事件触发器,那本质上也没必要事件委托了,因为 li 作为子节点,是最基础的结构,它本身就是目标对象,如果托管对象也是自己,这操作我是想不通为什么要这样做,直接给 li 绑定事件不是一回事嘛,最少也得托管到上级,比如 ul 。不过这样的话 currentTarget 方法也没法取得目标的 li 节点了,因为它等价于 this,也就是拿到的也是 ul 本身。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     983 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 26ms UTC 22:38 PVG 06:38 LAX 14:38 JFK 17:38
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86