【JavaScript】動的に挿入した要素に対応したイベント登録方法
jQueryでイベント登録する時には、動的に挿入した要素でもイベントが発火するようにするのは意識せずにやっているかと思います。
それを、PureなJavaScript、いわゆるVanillaJSで記述する方法を紹介します。
jQueryでクリックイベントを仕込む書き方
jQueryでクリックイベントを登録するときには、以下のようにonメソッドで登録していると思います。
$(function(){ $(document).on('click', '.hoge', function(e){ alert('Click!!!'); }); });
documentに対してクリックイベントを登録していますね。
documentにイベント登録したおかげで、画面のどこをクリックしてもイベントが発火するようになります。
第二引数に任意のクラス名を指定することで、指定したセレクタの要素をクリックした時だけ、第三引数の関数が実行されます。
VanillaJSでクリックイベントを仕込む書き方
クリックイベントを、jQuery, VanillaJS, それぞれで記述してみます。
まずは、セレクタにid属性を指定した場合です。
<button type="button" id="jQuery1">jQuery Click</button> <button type="button" id="JS1">JS Click</button> <script> $(function(){ $(document).on('click', '#jQuery1', function(e){ alert('jQuery Click'); }); }); document.addEventListener('click', function(e) { // id属性の値で判定 if (event.target && event.target.id === 'JS1') { alert('JS Click'); } }); </script>
event.target.id で id属性に指定した値が取得できるので、それでクリックした要素を判定しています。
次はセレクタにclass属性を指定する場合です。
<button type="button" class="jQuery2">jQuery2 Click</button> <button type="button" class="JS2">JS2 Click</button> <script> $(function(){ $(document).on('click', '.jQuery2', function(e){ alert('jQuery2 Click'); }); }); document.addEventListener('click', function(e) { // クラス属性の値で判定 if (event.target && event.target.classList.contains('JS2')) { alert('JS2 Click'); } }); </script>
event.target.classListでclass属性に指定されているクラス名の配列を取得することができます。
そして、contains() を使うと、classListに指定したセレクタが含まれているかどうかを判定することができます。
documentに対してイベント登録を行う方法は、イベントバブリングの仕組みを使っていますので、イベントバブリングが行われるイベントであれば、クリックイベント以外でも利用できます。
inputイベントを使う場合
例えば、clickイベントではなくinputイベントを指定してみましょう。
<input type="text" name="jQuery3" id="jQuery3"> <input type="text" name="JS3" id="JS3"> <script> $(function(){ $(document).on('input', '#jQuery3', function(){ alert('jQuery3 input'); }); }); document.addEventListener('input', function(e){ if (event.target && event.target.id === 'JS3') { alert('JS3 input'); } }); </script>
上記のコードは、テキストフィールドに文字を入力するたびにアラートが表示されます。
inputイベントはバブリングされるため意図した通りに実行されますが、中にはバブリングしないイベントもあります。
バブリングしないイベント
例えば、blurはバブリングしません。
そのため、以下のようにblurを利用しても発火してくれません。
<input type="text" name="jQuery4" id="jQuery4"> <input type="text" name="JS4" id="JS4"> <script> $(function(){ // 発火する $(document).on('blur', '#jQuery4', function(){ alert('jQuery4 blur'); }); }); // blurはバブリングしないので発火しない document.addEventListener('blur', function(e){ if (event.target && event.target.id === 'JS4') { alert('JS4 blur'); } }); </script>
こういう場面では、jQueryが裏側で解決してくれているので、やはりjQueryは使い勝手がよいですね。
ちなみに、focusoutはバブリングするイベントなので、上記のvanillaJSのblurをfocusoutに書き換えれば意図した動作になります。
// focusoutはバブリングするので発火する document.addEventListener('focusout', function(e){ if (event.target && event.target.id === 'JS4') { alert('JS4 focusout'); } });
もしくは、addEventListenerの第三引数のuseCaptureにtrueを指定すれば、blurでも発火させることができます。
キャプチャフェーズで発火させる
addEventListenerの第三引数は useCapter のフラグを指定できます。
trueを指定すると、キャプチャフェーズに実行することができるため、blurでもイベントを発火させることができます。
// useCapture: true の時はキャプチャフェーズに発火する document.addEventListener('blur', function(e){ if (event.target && event.target.id === 'JS4') { alert('JS4 blur'); } // ↓第三引数にtrue }, true);
useCaptureをtrueにする際に注意しておくべき点として、イベントの発火順があります。
addEventListenerの第三引数は省略した時はfalseとなり、バブリングフェーズに発火します。
キャプチャフェーズはバブリングフェーズより先に発生します。
同じ要素に複数のイベントを仕込んでいる場合に、かたや「useCapture:true」かたや「useCapture:false」ということをしていると、イベントの発火順を意識しておかないと意図しない挙動になりかねません。
useCapterをtrueにするときには、キャプチャフェーズ、バブリングフェーズの発火順に注意しましょう。