webpackerのお陰で最近jsを書いてるんですが、初歩的なところで悩んでいます。
動的に増える要素へのイベント設定をjQueryを使わずに書きたい。
// jQuery版
$(".foo").on("click", () => {
console.log("FOO↑↑↑");
})
// ES6版
const elements = document.querySelectorAll(".foo");
Array.from(elements, (element) => {
element.addEventListener("click", () => {
console.log("FOO↑↑↑");
})
})
.foo
なDOMがのちほどAjaxなどで動的に増えた時、jQuery版は動くけど、ES6版だと動かないじゃない?
下記のように書くというのをどこかで見たんですが、div全部取ってくるなんて遅そうで怖い。
const elements = document.getElementsByTagName("div");
Array.from(elements, (element) => {
if (element.classList.contains(".foo")) {
element.addEventListener("click", () => {
console.log("FOO↑↑↑");
})
}
});
どう書くのが普通なんでしょう?
動的に増える要素へのイベント設定は、Event Delegationと呼ばれるテクニックを使うと簡単です。これを使うとaddEventListener()の呼び出しは一度で済みます。jQueryは内部でこのテクニックを使っています。
https://davidwalsh.name/event-delegate
参考になれば幸いです。
window.addEventListener('click', evt => {
if(evt.srcElement.classList.contains('foo')) {
console.log("FOO↑↑↑");
}
});
ちなみにjQueryでの動的要素へのイベント設定もこの仕組みを使っています。
別解としては、Mutation Observerを使用する方法もありますが、APIの性質上、ちょっとシンプルなコードに収められません。
function fooClick (evt) {
console.log("FOO↑↑↑");
}
function mutationObjectCallback(records) {
records.forEach(record => {
// if(record.type === 'childList') ...// 基本的にはtypeをチェックするがobserve()するときにchildListのみ指定してるので省略
(record.addedNodes || []).forEach(node => {
if(node.classList.contains('foo')) {
node.addEventListener('click', fooClick);
}
});
}
}
var observer = MutationObserver(mutationObjectCallback);
observer.observe(document.body, {childList: true, subTree: true});