【问题标题】:Include child elements on click event listener on dynamically created elements在动态创建的元素上包含点击事件侦听器的子元素
【发布时间】:2017-09-25 00:23:22
【问题描述】:

https://jsfiddle.net/y444stqv/3/

如何在父“点击”事件侦听器中包含所有子元素?比如我动态创建如下html:

  <div id="content">
     <div id="donotexecuteclicklistener">But don't include me!</div>
      <!-- Dynamically generated -->
      <div class="items">
        <div class="item item-0">
          <h3>Item 0</h3>
          <p>Some desc</p>
        </div>

        <div class="item item-1">
          <h3>Item 1</h3>
          <p>Some desc</p>
        </div>

        <div class="item item-2">
          <h3>Item 2</h3>
          <p>Some desc</p>
        </div>

        <div class="item item-3">
          <h3>Item 3</h3>
          <p>Some desc</p>
        </div>
      </div>
    </div>

我想为每个“项目”div 绑定一个点击处理程序。因此,如果您单击任何属于“item”子节点的元素(即子“h3”或“p”标签)节点,我仍然想执行“handleClick”单击处理程序。但是,当您单击 ID“donotexecuteclicklistener”时,我不希望触发该单击处理程序。

  var items = [];
  var $content = document.getElementById('content');

  for (var i = 0; i < 4; i++) {
     // using ES6 template literals for ease
     var item = `
        <div class="item" id="item-${i}">
            <h3>Item ${i}</h3>
            <p>Some desc about item ${i}</p>
        </div>
     `;

     items.push(item);
  }

  var html = items.join(' ');
  $content.innerHTML = html;

  var handleClick = function(event) {
      console.log('do something with item', this, event);
  }

  $content.addEventListener('click', function(event) {
      console.log(event.target.id);
      var elements = $content.querySelectorAll('.item');
      var hasMatch = Array.prototype.indexOf.call(elements, event.target) >= 0;
      console.log(hasMatch);

      if (hasMatch) {
        handleClick.call(event.target, event);
      }
  })

【问题讨论】:

  • "我想使用事件委托来绑定点击处理程序......" 你误用了“事件委托”这个表达,它是关于捕获冒泡事件,不附加侦听器。请在此处发布可运行代码,不要在其他地方发布。

标签: javascript dom


【解决方案1】:

这是默认发生的!当一个元素在子元素上触发时,它会“冒泡”到父元素。您可以看到 click 事件与 Event.bubbles 属性 (https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles) 一起冒泡。

元素是动态生成的这一事实不会影响此行为:事件仍会在动态生成的元素上冒泡。

为了获取事件发生的元素,可以使用e.target

ancestor.addEventListener('click', (e) => {
    const element = e.target;
});

小心:e.stopPropagation() 方法取消了这个冒泡功能,所以你需要确保你没有在任何地方调用它。如果您无法让事件冒泡,这可能是罪魁祸首。

【讨论】:

    【解决方案2】:

    要充分利用事件委托的概念,请将单个侦听器附加到 content 元素,然后查看事件来自何处以确定响应。

    如果它来自具有类 item 的 div 内的元素,则执行任何操作,例如

    function handleClick(e){
      var el = e.target;
      var div = getParentByClass(el, 'item');
      if (div) {
        console.log(div.className);
      } else {
        console.log('no item parent')
       }
    }
    
    // Get self or first parent with particular class
    // May be replaced by Element.closest in future
    function getParentByClass(el, className) {
      do {
        if (el.classList.contains(className)) {
          return el;
        } else {
          el = el.parentNode;
        }
      } while (el && el.parentNode)
    }
    
    
    window.onload = function(){
      document.getElementById('content').addEventListener('click', handleClick, false);
    }
    //*/
    <div id="content">
         <div id="donotexecuteclicklistener">But don't include me!</div>
          <!-- Dynamically generated -->
          <div class="items">
            <div class="item item-0">
              <h3>Item 0</h3>
              <p>Some desc</p>
            </div>
    
            <div class="item item-1">
              <h3>Item 1</h3>
              <p>Some desc</p>
            </div>
    
            <div class="item item-2">
              <h3>Item 2</h3>
              <p>Some desc</p>
            </div>
    
            <div class="item item-3">
              <h3>Item 3</h3>
              <p>Some desc</p>
            </div>
          </div>
        </div>

    另见Element.closest。您也可以考虑更通用的“通过选择器获取父级”,但性能可能会更慢,例如

    function getParentBySelector(el, selector) {
      var node;
      var els = document.querySelectorAll(selector);
      els = els && els.length? Array.from(els) : [];
    
      while (el && el.parentNode) {
        el = el.parentNode;
        node = els.find(x => x === el);
        if (node) return node;
      }   
    }
    

    并通过以下方式调用它:

    var div = getParentBySelector(el, 'div.item');
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-28
      • 2016-07-22
      • 2022-11-24
      • 2012-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多