事件委托,如何准确限制事件发生在想要发生的元素上

<ul class="buy-list">
     <li class="item">
        <div class="sour-price">
           <div class="source">京东商城</div>
           <div class="price">¥1140</div>
        </div>
        <div class="desc-buylink">
           <div class="desc">京东商城</div>
           <div class="buylink">购买</div>
        </div>
     </li>
     ...
     ...
</ul>

上面的结构,ul下面有很多li,现在我把事件委托到ul上,

代码:

var buyList = document.querySelector('.buy-list');

buyList.onclick = function(e){
     
     var target = e.target;
     
     // 只有点击li才触发事件
     
     if(target.className.indexOf('item') !== -1){
      
          // do something       
     
     }
     

}

我想实现的效果是:点击li或li内部的任意一个元素,都会执行事件。

但是上面的写法只有当我点击li时才会执行do something,点击的是sour-price 或 price元素,都不会执行do something...

请问如何实现我想要的效果?

PS:当然,判断 target.parentNodetarget.parentNode.parentNode看看是不是li也是可以的,但老觉得这么写不优雅。假设li里的结构更加复杂的话,这样写就不行了,总不能N个parentNode,一直往上找吧?

阅读 7.4k
13 个回答

首先要用 addEventListener 而不用 onclick, onclick只能绑定一个事件,前者可以绑定多个。

明确的回答你用jQuery,其中有相应的用法,.on()

搞清楚了 DOM 事件处理流程,其实永原生 JS 解决问题也不难。

80733bd4gw1f09tjjbi1zj20pi0orgqz.jpg
via:dom-event-architecture

你没说清楚点击后执行什么样的事件?子元素的事件是会冒泡到父元素的,你在父元素上添加事件处理即可。

addEventListener用这个给li绑定事件

判断target,当前触发事件的

function isInNodes(nodes,child){
    if(!_Type.isArray(nodes) || child.nodeType !== 1) return -1;
    var i = 0,l = nodes.length;
    while(i < l){
        if(nodes[i].nodeType === 1 && nodes[i].contains( child ) ) return i;
        i++;
    }
    return -1;
}

//判断child是否在nodes中,或子元素;返回Number||-1
这个是我用的,nodes = 所有的li,child = e.target 对象,判断点击的对象是li的哪个子元素

onclick只是你绑定的DOM本身的一个事件回调(方法),并不涉及事件传递,所以我觉得这也并不能称为事件委托。委托还是要用addEventListener的(仅就原生来说,jQuery里就是on)

看题主,应该是想如何有简单的方法判断element1是否是element2的descendant,答案就是自己写一个利用parentNode的函数,详见stackoverflow

应该是事件冒泡问题引起的,同意zach5078的方案

var buyList = document.querySelector('.buy-list');
var findli = function(e) {
  var el = e.target;
  while(el.tagName !== 'LI') {
    el = el.parentNode;
    if (!el) {
        break;
    }
  }
  return el;
};
buyList.onclick = function(e){
     var target = e.target;
     var li = findli(e);
     if (li) {
       // 动作
     }

}

难道你的ul标签里还有不是li元素的直接子元素??那就说明你的HTML就是不规范的

新手上路,请多包涵

事件绑定在UL上,怎么让点击底下的每个LI执行不同的操作啊?

这样就可以了:

$('.buy-list').on('click','.item',function(){
    //do something...
});

可实现一次绑定给父元素$('.buy-list')下所有的目标子元素.item

if (!Element.prototype.matches) {
  Element.prototype.matches =
    Element.prototype.matchesSelector ||
    Element.prototype.mozMatchesSelector ||
    Element.prototype.msMatchesSelector ||
    Element.prototype.oMatchesSelector ||
    Element.prototype.webkitMatchesSelector ||
    function(s) {
      var matches = (this.document || this.ownerDocument).querySelectorAll(s),
      i = matches.length;
      while (--i >= 0 && matches.item(i) !== this) {}
      return i > -1;
    };
    
 document.getElementById('list').addEventListener('click', function (e) {
  // 兼容性处理
  var event = e || window.event;
  var target = event.target || event.srcElement;
  if (target.matches('li.class-1')) {
    console.log('the content is: ', target.innerHTML);
  }
});
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题