セレクタで絞り込んだリスナを簡単に作りたい
- カテゴリ:
- JavaScript
- コメント数:
- Comments: 0
◆ eve.scope(".selector", elem => {})
◆ jQuery の on みたいなことをする別のやり方
◆ jQuery の on みたいなことをする別のやり方
追加・削除できる要素の×ボタンのクリックなど リスナをつけたい要素が動的に増えるつくりだと 追加されるたびにリスナを設定する必要があります
それを避けるためにリスナ自体はそれらの親クラス もしくは全体の親として絶対に存在する body や document, window につけておくことがあります
jQuery でいう on に第二引数を指定するときです
リスナが親要素に設定されているので 関係ない要素のクリックでも呼び出されますが イベントの発生元が指定したセレクタ以下じゃないときは何もしません
生の JavaScript だとこの機能はないので window にリスナをつけた場合だと 関係ないイベントを無視する if 文が必要です
addEventListener の代わりに on を使えるように prototype を拡張して置くと楽ですが 個人的に on だと 良くないなと思うところもあります
本来リスナをつけたい数だけ on でリスナをつけるので window や document にいっぱいのリスナがついて何度も関数呼び出しが実行されます
それに コードによってはあちこちに window へのリスナ設定が散らばってしまって管理しづらくなったりします
そういう意味では on と同等の機能を作るより addEventListener の中で場合分けしてるほうが読みやすいように思います
window につけるものでまとめるのではなく コンポーネントごとにリスナ設定をまとめたいという場合もありますが今回はそれは対象外です
それと個人的に on を使うと target/currentTarget/delegateTarget で困ります
currentTarget/delegateTarget は毎回のようにググるほどどっちがどっちかわかりません
そんなことも含めて作ってみたのがこちら
リスナ引数の event に対して scope メソッドをセレクタとコールバック関数で呼び出します
セレクタのスコープ内でイベントが起きていた場合のみ セレクタにマッチした要素を引数としてコールバック関数が呼び出されます
target/currentTarget は通常のリスナのものなので迷う心配はありません
window や body などのリスナ内に eve.scope(~~) を並べるので実行順も見たままです
今回は実装してないですが scope の返り値で stopImmediatePropagation の動きをできるようにしておけばもうちょっと便利になりそうです
使った DOM 構造
scope メソッドの定義
isArray や instanceof でチェックしてるのは CustomEvent などで想定外のものが来てもいいようにするためで基本は配列になってるはずです
それを避けるためにリスナ自体はそれらの親クラス もしくは全体の親として絶対に存在する body や document, window につけておくことがあります
jQuery でいう on に第二引数を指定するときです
リスナが親要素に設定されているので 関係ない要素のクリックでも呼び出されますが イベントの発生元が指定したセレクタ以下じゃないときは何もしません
生の JavaScript だとこの機能はないので window にリスナをつけた場合だと 関係ないイベントを無視する if 文が必要です
addEventListener の代わりに on を使えるように prototype を拡張して置くと楽ですが 個人的に on だと 良くないなと思うところもあります
本来リスナをつけたい数だけ on でリスナをつけるので window や document にいっぱいのリスナがついて何度も関数呼び出しが実行されます
それに コードによってはあちこちに window へのリスナ設定が散らばってしまって管理しづらくなったりします
そういう意味では on と同等の機能を作るより addEventListener の中で場合分けしてるほうが読みやすいように思います
window につけるものでまとめるのではなく コンポーネントごとにリスナ設定をまとめたいという場合もありますが今回はそれは対象外です
それと個人的に on を使うと target/currentTarget/delegateTarget で困ります
currentTarget/delegateTarget は毎回のようにググるほどどっちがどっちかわかりません
そんなことも含めて作ってみたのがこちら
window.addEventListener("click", eve => {
eve.scope(".delete", elem => {
console.log("eve.target: ", eve.target) // input
console.log("eve.currentTarget: ", eve.currentTarget) // window
console.log("elem: ", elem) // div
console.log("==")
})
})
eve.scope(".delete", elem => {
console.log("eve.target: ", eve.target) // input
console.log("eve.currentTarget: ", eve.currentTarget) // window
console.log("elem: ", elem) // div
console.log("==")
})
})
リスナ引数の event に対して scope メソッドをセレクタとコールバック関数で呼び出します
セレクタのスコープ内でイベントが起きていた場合のみ セレクタにマッチした要素を引数としてコールバック関数が呼び出されます
target/currentTarget は通常のリスナのものなので迷う心配はありません
window や body などのリスナ内に eve.scope(~~) を並べるので実行順も見たままです
今回は実装してないですが scope の返り値で stopImmediatePropagation の動きをできるようにしておけばもうちょっと便利になりそうです
使った DOM 構造
document.body.innerHTML = `
<section>
<p>aaa</p>
<div class="delete"><input type="button"></div>
</section>
`
<section>
<p>aaa</p>
<div class="delete"><input type="button"></div>
</section>
`
scope メソッドの定義
Event.prototype.scope = function(selector, fn){
if(Array.isArray(this.path)){
for(let elem of [...this.path]){
if(elem instanceof HTMLElement && elem.matches(selector)){
fn(elem)
return
}
if(elem === this.currentTarget){
return
}
}
}
}
if(Array.isArray(this.path)){
for(let elem of [...this.path]){
if(elem instanceof HTMLElement && elem.matches(selector)){
fn(elem)
return
}
if(elem === this.currentTarget){
return
}
}
}
}
isArray や instanceof でチェックしてるのは CustomEvent などで想定外のものが来てもいいようにするためで基本は配列になってるはずです