できる気になっているJSを改めてチュートリアルからやってみる 12日目

~~ JavaScriptのイベント ~~

最近ちょっとずつまた学びなおす必要が出てきたなぁと思い、いろいろプログラムを勉強しなおしているところなんですが、実はもう今使っている知識は古いのかもと思って、アップデートしようとおもいやってみる会。

実施するのは、この記事

MDN - Javascript

完全な初心者向けと書かれたチュートリアルは全然初心者向けではないって話。アップデートしていきましょう。

JavaScript の構成要素
をやってます。

今回はイベント。

addEventListener , removeEventListener

これと直接イベント名に埋め込む方法との違いは、複数の関数を同じイベントに登録するか否か。

直接、イベントに登録する場合は最後に書かれている関数が実行されます。下記の例だとFunctionBのみが実行されます。

myElement.onclick = functionA;
myElement.onclick = functionB;

addEventListener は、複数関数登録できて、それぞれ実行されます。
下記の場合は、どちらも実行されます。

myElement.addEventListener('click', functionA);
myElement.addEventListener('click', functionB);

それぞれ、どう使い分けるかですが古いバージョンに対応しているのは前者ですが、後者は登録した関数を削除することができるというのも大きな強みになります。

イベントオブジェクト

イベントハンドラー関数内の、event、evt、単に eなどはイベントオブジェクトと呼ばれて、イベントの追加機能や情報を提供する目的でイベントハンドラーに自動的に渡されます。

イベントオブジェクトには好きな名前を使えます。使い方は、イベントハンドラー関数のかっこの中に使いたい名前を書くだけです。

<!DOCTYPE html>
<html>
 <head>
   <meta charset="utf-8">
   <title>Random color event object example</title>
   <style>
     button {
       margin: 10px;
       font-size: 300%;
       padding: 30px;
     };
   </style>
 </head>
 <body>
   <button>Change color</button>
   <script>
     const btn = document.querySelector('button');

     function random(number) {
       return Math.floor(Math.random()*number);
     }

     function bgChange(e) {
       const rndCol = 'rgb(' + random(255) + ',' + random(255) + ',' + random(255) + ')';
       e.target.style.backgroundColor = rndCol;
       console.log(e);
     }

     btn.addEventListener('click', bgChange);
   </script>
 </body>
</html>

複数の要素に同じイベントハンドラを割り当てて、どれかでイベントがあったときにはe.targetで指定できる。これは使おう、逆になんで知らなかったんだ。これから楽できるぞー。

そのほかのイベント

もっと上級者向けハンドラーでは、動作に必要な追加データを保持するために特殊なプロパティを付与するものもあります。例えば Media Recorder API には dataavailable イベントがあり、オーディオやビデオの録音や再生が終わって何か(保存したり再生したり)する準備ができたところで発火します。これに紐付く ondataavailable ハンドラーのイベントオブジェクトには録音・録画データを保持する data プロパティがあり、これを使って何かしらを行なえます。

だそうです。

標準の動作を抑制する

<form>
 <div>
   <label for="fname">First name: </label>
   <input id="fname" type="text">
 </div>
 <div>
   <label for="lname">Last name: </label>
   <input id="lname" type="text">
 </div>
 <div>
    <input id="submit" type="submit">
 </div>
</form>
<p></p>

というようなものがあって、空入力の場合、送信しないようにする方法です。

const form = document.querySelector('form');
const fname = document.getElementById('fname');
const lname = document.getElementById('lname');
const para = document.querySelector('p');

form.onsubmit = function(e) {
 if (fname.value === '' || lname.value === '') {
   e.preventDefault();
   para.textContent = 'You need to fill in both names!';
 }
}

イベントオブジェクトの preventDefault() 関数 でフォームの送信を抑制します。

イベントのバブリングとキャプチャリング

遭遇することがすくないですが、ある一つの要素で同じイベントに紐づく二つのハンドラが活性化されたときに何が起きるのかを説明するのが、イベントのバブリングとキャプチャリング二種類のメカニズムです。

<button>Display video</button>

<div class="hidden">
 <video>
   <source src="rabbit320.mp4" type="video/mp4">
   <source src="rabbit320.webm" type="video/webm">
   <p>Your browser doesn't support HTML5 video. Here is a <a href="rabbit320.mp4">link to the video</a> instead.</p>
 </video>
</div>

<button> がクリックされると、<div> のクラス属性を hidden から showing に変更するので、ビデオが表示されます(例の CSS にこの二つのクラスが含まれており、それぞれはボックスの位置をスクリーンの外、内にします)。

btn.onclick = function() {
 videoBox.setAttribute('class', 'showing');
}

videoBox.onclick = function() {
 videoBox.setAttribute('class', 'hidden');
};

video.onclick = function() {
 video.play();
};

という感じで、イベントのハンドラーを登録すると、videoBoxをクリックすると再生はされるけどdivが隠されるようになりました。

これは、親要素を持つ要素においてイベントが発火すると、モダンブラウザーは二つの異なる段階に分けて動作します。

キャプチャリング

- 要素の最上位の親要素(<html> にonclickイベントハンドラーがキャプチャリング段階に登録されているか調べ、あればそれを実行します。
- 次に<html>要素の内側の要素に移って同じことをし、また次の内側の要素にと、実際にクリックされた要素に到達するまで繰り返されます。

バブリング

- ブラウザーは実際にクリックされた要素の onclick イベントハンドラーがバブリング段階に登録されていれば、それを実行します。
- 次に直上の親要素に移動して同じ事をし、また次へ、 要素に到達するまで繰り返します。

というわけで、モダンブラウザーのデフォルトでは、全てのイベントハンドラーはバブリング段階に登録されます。ですのでこの例の場合では、ビデオをクリックするとクリックイベントは <video> 要素から外側の <html> 要素に進んで (バブリングして) いきます。

video.onclick... ハンドラーがあるので実行し、最初ビデオが始まります。
videoBox.onclick... ハンドラーがあるので実行し、よってビデオも隠されます。

んでこれは、 stopPropagationで問題を解決できます。

いいなと思ったら応援しよう!