イベントのキャンセル
イベントのキャンセルについて試してみます。
イベントのキャンセルは以下の3つの書き方(メソッド)があり、それぞれイベント伝播がキャンセルされます。
preventDefault stopPropagation stopImmediatePropagation
キャンセルの方法はそれぞれ異なる動作仕様にもとづいてキャンセルされます。
それぞれのメソッドを詳しくみていきます。
preventDefaultについて
preventDefaultはデフォルトのイベントをキャンセルします。
ここでいうデフォルトとは、「aタグ」の場合、リンクを押下した時の動き(画面遷移)をキャンセルできます。
実際に下記のサンプルを書いて試してみます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <div id="test1l"> <div> <a href="./test1linktest.html" target="blank" id="test1link">リンクテスト1</a> </div> <div> <a href="./test1linktest.html" target="blank" id="test2link">リンクテスト2</a> </div> </div> <script type="text/javascript"> // リンク要素のDOMを取得 let element_test1link = document.getElementById('test1link'); // 外部関数化せず無名関数で書いてもOK element_test1link.addEventListener('click', testclick1link, false); function testclick1link(event) { // preventDefaultの検証 alert('testclick1link preventDefault START'); event.preventDefault(); alert('testclick1link preventDefault END'); } </script> </body> </html>
HTMLにアクセスすると、画面上には
<a href="./test1linktest.html" target="blank" id="test1link">リンクテスト1</a>
と
<a href="./test1linktest.html" target="blank" id="test2link">リンクテスト2</a>
が表示されます。
「リンクテスト1」のほうには、イベントを登録してあり、その中で「preventDefault」メソッドを呼んでいます。
「リンクテスト2」には特にイベントを登録していません。
「リンクテスト1」と「リンクテスト2」をそれぞれクリックしてみると、
「リンクテスト1」のほうはアラートが2回表示され、その後は何も変化しません。(a hrefの機能が抑止されている)
「リンクテスト2」のほうは、イベントは登録していないので、そのまま確認用のテスト用HTMLへ画面遷移することがわかります。
このようにpreventDefault()はDOM要素のデフォルトの動きを抑止する働きがあります。
aタグ以外にもデフォルトのイベントを持つDOM要素は全て同様に抑止されます。
stopPropagationについて
イベントの伝播を抑止します。
同じイベントハンドラが他のDOMに定義されている場合(ネストされている場合)、子要素のイベントの処理で、stopPropagationを呼び出した後は、親要素のイベントハンドラは呼び出されません。
伝播については、キャプチャリングでもバブリングでも自分自身でも抑止されます。
具体的なサンプルを書いて試してみます。
前回の投稿「イベントの伝播(2022/06/10)」では、下記のHTMLを用意しました。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <div id="test1p"> テスト親 <div id="test1c"> テスト子 </div> </div> <script type="text/javascript"> // 親要素にクリックイベントを登録 let element_test1p = document.getElementById('test1p'); element_test1p.addEventListener('click', testclick1p, false); function testclick1p() { alert('on p !'); } // 子要素にクリックイベントを登録 let element_test1c = document.getElementById('test1c'); element_test1c.addEventListener('click', testclick1c, false); function testclick1c() { alert('on c !'); } </script> </body> </html>
画面上の「テスト子」をクリックすると、子要素のクリックイベントが動き、その後に親要素のクリックイベントも動きます。
ではこのサンプルの 子要素側の処理に stopPropagation を呼ぶように変更してみます。
以下のHTMLを用意しました。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <div id="test1p"> テスト親 <div id="test1c"> テスト子 </div> </div> <script type="text/javascript"> // 親要素にクリックイベントを登録 let element_test1p = document.getElementById('test1p'); element_test1p.addEventListener('click', testclick1p, false); function testclick1p() { alert('on p !'); } // 子要素にクリックイベントを登録 let element_test1c = document.getElementById('test1c'); element_test1c.addEventListener('click', testclick1c, false); function testclick1c(event) { alert('on c !'); event.stopPropagation(); } </script> </body> </html>
テスト子のメソッド内で
event.stopPropagation();
という追記しています。
アクセスして「テスト子」をクリックすると、先程のサンプルとは違い、「on c !」のアラートのみが表示され、
「on o !」のアラートは表示されません。
stopPropagation()が実行されて親要素にイベントが伝播されなくなったことがわかります。
stopImmediatePropagationについて
同じDOM要素に対して、複数の同じイベントハンドラ(処理内容は別)が定義されている場合、
任意のイベント処理で stopImmediatePropagation を呼び出すと、その他のイベントハンドラは呼び出されません。
伝播についてもstopPropagationと同様に抑止されます。
具体的に次のHTMLを用意して試してみます。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <div id="test1p"> テスト親 </div> <script type="text/javascript"> // 要素を取得 let element_test1p = document.getElementById('test1p'); // 要素にクリックイベント1を登録 element_test1p.addEventListener('click', testclick1p, false); function testclick1p(event) { alert('on p 1 !'); } // 要素にクリックイベント2を登録 element_test1p.addEventListener('click', testclick2p, false); function testclick2p(event) { alert('on p 2 !'); } // 要素にクリックイベント3を登録 element_test1p.addEventListener('click', testclick3p, false); function testclick3p(event) { alert('on p 3 !'); } // 要素にクリックイベント4を登録 element_test1p.addEventListener('click', testclick4p, false); function testclick4p(event) { alert('on p 4 !'); } </script> </body> </html>
画面にアクセスして「テスト親」をクリックすると「on p 1 !」から「on p 4 !」まで、4回アラートが表示されます。
では、このHTMLに stopImmediatePropagation を追加してみます。
わかりやすいように3回目のアラート後に追記します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>テストHTML</title> </head> <body> <div id="test1p"> テスト親 </div> <script type="text/javascript"> // 要素を取得 let element_test1p = document.getElementById('test1p'); // 要素にクリックイベント1を登録 element_test1p.addEventListener('click', testclick1p, false); function testclick1p(event) { alert('on p 1 !'); } // 要素にクリックイベント2を登録 element_test1p.addEventListener('click', testclick2p, false); function testclick2p(event) { alert('on p 2 !'); } // 要素にクリックイベント3を登録 element_test1p.addEventListener('click', testclick3p, false); function testclick3p(event) { alert('on p 3 !'); event.stopImmediatePropagation(); } // 要素にクリックイベント4を登録 element_test1p.addEventListener('click', testclick4p, false); function testclick4p(event) { alert('on p 4 !'); } </script> </body> </html>
クリックイベント3の処理内で
event.stopImmediatePropagation();
と書くことでイベントの伝播を抑止しています。
画面にアクセスして「テスト親」をクリックすると「on p 1 !」「on p 2 !」「on p 3 !」までアラート表示され、
「on p 4 !」のイベント処理が抑止されていることがわかります。