やりたいこと
任意の点をクリックしたとき、重なっている要素すべてを取得したい。
( 例えば x をクリックした場合は A, B, D を取得)
それぞれの要素は position: absolute
などで位置が指定されており、親子関係にない。
そのためイベントのバブリングでは実現できない、というケース。
document.elementsFromPoint
こういう場合は document.elementsFromPoint
を使うと楽に取得できる。
MDNによると「実験的な機能」らしいので、念のためにポリフィルを導入する。
サンプルコード
click イベントを範囲広めに設定して、 clientX
clientY
を elementsFromPoint
に渡すだけ。超らくちん。
ただし、 elements には body や html 要素も含まれるので、不要なら自前で取り除く必要がある。
document.addEventListener('click', e => {
const elements = document.elementsFromPoint(e.clientX, e.clientY)
elements
.filter(element => {
// 不要な要素は自前で取り除く
const tagName = element.tagName.toLowerCase()
return !['html', 'body'].includes(tagName)
})
.forEach(element => {
// 何かの処理
})
})
--
もしそれぞれの要素の click イベントを発火させたい場合は filter で e.target
を除いてから dispatchEvent
を呼ぶと良さげ。
(そうしないとイベントが二重に呼ばれちゃうから)
document.addEventListener('click', e => {
const elements = document.elementsFromPoint(e.clientX, e.clientY)
elements
.filter(element => {
return element !== e.target
})
.forEach(element => {
element.dispatchEvent(new MouseEvent('click'))
})
})
document.getElementById('A').addEventListener('click', e => { alert('A') })
document.getElementById('B').addEventListener('click', e => { alert('B') })
document.getElementById('C').addEventListener('click', e => { alert('C') })
document.getElementById('D').addEventListener('click', e => { alert('D') })