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);
}
})
答案 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');