我有两个占据相同单元格的网格子级。它们都具有click
事件侦听器。根据event bubbling,当我单击单元格时,应同时触发两个事件侦听器。
const outer = document.getElementById('outer')
outer.addEventListener('click', () => console.log('on outer click'))
const inner1 = document.getElementById('inner1')
inner1.addEventListener('click', () => console.log('on inner1 click'))
const inner2 = document.getElementById('inner2')
inner2.addEventListener('click', () => console.log('on inner2 click'))
#outer {
width: 100vw;
height: 100vh;
display: grid;
}
#inner2 {
grid-column: 1/2;
grid-row: 1/2;
}
#inner1 {
grid-column: 1/2;
grid-row: 1/2;
}
<div id="outer">
<div id="inner1">
</div>
<div id="inner2">
</div>
</div>
游乐场:https://jsfiddle.net/o10sfpwu/
预期日志:
on inner2 click
on inner1 click
on outer click
实际记录:
on inner2 click
on outer click
为什么未触发inner1事件侦听器? click事件应该冒泡占据该空间的所有元素,对吧?
click事件应该冒泡占据该空间的所有元素,对吗?
click事件(或任何事件)将仅在元素的DOM祖先中冒泡。事件冒泡时,不考虑元素的位置,也不考虑位置是否与另一个元素重叠。
在这种情况下,由于inner2
与inner1
位于同一单元格,并且inner2
放在DOM中的inner1
之后,因此inner2
将位于inner1
的顶部。因此,单击该单元格将导致一个事件被调度到inner2
。然后,要查看冒泡时还有哪些其他元素看到了该事件,请查看inner2
的祖先:
<div id="outer">
<div id="inner1">
</div>
<div id="inner2">
</div>
</div>
但是它的祖先只有#outer
(以及document.body
和document.documentElement
)。 inner1
不是祖先,并且该事件已调度到inner2
,因此该事件不会通过inner1
冒泡。
在your MDN link中对此进行了描述:
冒泡阶段:
浏览器会检查在冒泡阶段实际单击的元素是否在其上注册了onclick事件处理程序,如果是,则运行该事件处理程序。
然后它前进到下一个直接祖先元素并执行相同的操作,然后执行下一个操作,依此类推,直到到达该元素。
[其他答案已经正确回答了您的问题,我只是滥用答案表格将您(以及将来的读者)指向document.elementsFromPoint,我认为这实际上是您想要的:
const outer = document.getElementById('outer')
outer.addEventListener('click', (evt) => {
const nodes = document.elementsFromPoint(evt.clientX, evt.clientY);
nodes.forEach( (node) => console.log( 'clicked over', node));
});
#outer {
width: 100vw;
height: 100vh;
display: grid;
}
#inner2 {
grid-column: 1/2;
grid-row: 1/2;
}
#inner1 {
grid-column: 1/2;
grid-row: 1/2;
}
<div id="outer">
<div id="inner1">
</div>
<div id="inner2">
</div>
</div>