在动态创建的元素

时间:2017-09-25 00:23:23

标签: javascript dom

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

如何在父“click”事件侦听器中包含所有子元素?例如,我动态创建以下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>

我想将点击处理程序绑定到每个“item”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);
      }
  })

2 个答案:

答案 0 :(得分: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()方法会取消此冒泡功能,因此您需要确保不在任何地方调用它。如果你无法让事件发生泡沫,这可能是罪魁祸首。

答案 1 :(得分:0)

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

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

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');
相关问题