Code Journey

30代未経験からプログラミング挑戦中(追うものは追われる者に勝る)

【初学者用まとめ(イベント処理編)】まず覚えるべきJavaScriptのコードの書き方(自習記録)

はじめに

前提

まず前提として、この記事を書いているのは未経験からプログラミングに挑戦中の者になります。学習はフィヨルドブートキャンプに参加しながら取り組んでいて、現役のエンジニアの方からレビューをもらいながら進めています。

現在の学習状況は以下を随時更新しているので興味ある方はご覧ください。

hirano-vm4.hatenablog.com

筆者は執筆時点で、RubyRuby on Rails)のプラクティスが終わったあとにJavaScriptに取り組んでいるので、完全に0からではありません。JavaScriptを多言語から学ぶにあたって、まず覚えるべき要点をまとめました。

詳しい解説ではなく要点をまとめた学習記録・ノートになりますのでご注意ください。

記事の本体は以下にになります。長くなってしまったので別記事として書いているものとなります。ご注意ください。

hirano-vm4.hatenablog.com

イベント処理

  • JavaScriptを使うとWebページ上でイベントが発生したときに実行する処理や関数を実行させることができる。イベントが発生した時に実行する処理や関数のことをイベントハンドラと呼ぶ

  • イベントは Web ページ上でマウスをクリックしたりキーボードのキーを押したときや、表示しようとしている Web ページの読み込みが完了した場合などに発生

HTML要素の属性としてイベントハンドラを登録

input 要素でマウスでクリックしたときに実行するイベントハンドラを登録する場合

<input type="button" value="button" onclick="イベントハンドラ">
  • onclick 属性に対する値としてイベントハンドラを記述(JavaScript のコードや関数を記述)
  • 複数の文を記述することもできる(最後の文以外には末尾にセミコロン(;)を記述)
  <body>
    <p>ボタンをクリック</p>

    <input type="button" value="button" onclick="alert('こんな感じ')" />
  </body>

イベントの種類の一覧

属性 イベントの種類
onclick マウスのボタンが要素の上でクリックされたとき
oncontextmenu マウスが右クリックされたとき
ondblclick マウスのボタンが要素の上でダブルクリックされたとき
onmousedown マウスが要素の上で押されているとき
onmouseup マウスのボタンが要素の上で離されたとき
onmousemove マウスが要素の上を移動しているとき
onmouseover マウスがリスナーが接続されている要素または子要素に移動したとき
onmouseout マウスがリスナーが接続されている要素または子要素から外れたとき
onselect テキストが選択されているとき
onwheel マウスホイールが回転しているとき
onauxclick マウスのメイン以外のボタン(中央のボタンなど)が要素の上でクリックされたとき
onkeydown いずれかのキーが押されたとき
onkeyup いずれかのキーが離されたとき
onkeypress いずれかのキーが押された状態にあるとき
onblur 要素がフォーカスを失ったとき(バブリングなし)
onfocus 要素がフォーカスを受け取ったとき(バブリングなし)
onchange 要素の値の変更が確定したとき(<input>, <select>, <textarea>)
oninput 要素の値が変更したとき(<input>, <select>, <textarea>)
onsubmit サブミットボタンが押されたとき
onreset リセットボタンが押されたとき
onload ページ全体がスタイルシートや画像などのすべて読み込まれたとき
onabort エラー以外の原因で読み込みが中止されたとき
onerror エラーが発生して読み込みが失敗したとき

バブリング・キャプチャリングとは?

ウェブページ上にユーザーが何かイベントをトリガーしたとき(例:ボタンをクリック・要素にマウスを載せるなど)、そのイベントはHTML要素のツリー構造をたどって(例:イベントが発生した要素の親要素である div 要素から、 body 要素、 html 要素、 Document オブジェクト、 Window オブジェクトまで DOM ツリーを上へ向かって順番に click イベントが発生)下から上へ伝播することを「バブリング」と呼ぶ。

  • イベントは通常、ユーザーの操作が行われた要素から親要素、さらに祖父要素へと伝播する
  • 親要素から子要素への逆伝播はキャプチャリングという
  • イベントは要素の境界を超え、親要素にも伝播する。親要素でイベントハンドラを設定されている場合、そのハンドラも実行される
  • バブリングはイベントハンドリングの一貫性を提供する。親要素で1つのイベントハンドラを設定することで、子要素すべてに対して同じ処理を行えて、コードの効率性と保守性に役立つ

DOMで取得した要素のプロパティにイベントハンドラを登録

要素オブジェクトのプロパティにイベントハンドラを登録

これまでの例ではHTMLの中でイベントハンドラを設定していたが、DOMを使用して任意の要素オブジェクトとして取得。その要素オブジェクトに対してイベントハンドラを設定できる。

  <body>
    <input type="button" value="button" id="test" />

    <script>
      const buttonClick = () => {
        alert("これを表示させる");
      };

      let button = document.getElementById("test");
      button.onclick = buttonClick;
    </script>
  </body>

  • HTML コードで記述された input タグには JavaScript のコードが含まれなくなり、イベントの処理は JavaScript のコードの中で完結する
イベントハンドラにコールバック関数を設定する
  • プロパティにイベントハンドラを設定する場合は、イベントが発生した時に呼び出されるコールバック関数を設定
  • click イベントに対してコールバック関数の buttonClick 関数を設定している
  • コールバック関数を設定するときには、関数名だけを記述する
    <script>
      const buttonClick = () => {
        let day = new Date();
        alert(day);
      };
      let button = document.getElementById("test");
      button.onclick = buttonClick;
    </script>
  </body>
イベントハンドラを無名関数やアロー関数を使って記述

イベントハンドラとして登録するコールバック関数は、無名関数を使って記述することもできる。

    <script>
      let button = document.getElementById("test");
      button.onclick = function () {
        let day = new Date();
        alert(day);
      };
    </script>
イベントハンドラを解除する
button.onclick = null;
    <script>
      let button = document.getElementById("test");
      button.onclick = function () {
        let day = new Date();
        alert(day);

        // 1度呼び出したら解除する
        this.onclick = null;
      };
    </script>

こうすると1度アラートが表示され、二回目以降は何も起こらなくなる。

※無名関数の場合、関数内で this を参照するとイベントが発生した要素が格納されている

コールバック関数が呼び出される時にイベントの情報を受け取る
  • イベントが発生してコールバック関数が呼ばれるときに発生したイベントに関する情報が格納された Event オブジェクトが引数として渡されてくる
  • イベントに関する情報を関数内で利用する場合は、コールバック関数で引数を記述する
<script>
    function butotnClick(event){
        // 何かしらの処理
    }

    let button = document.getElementById('test');
    button.onclick = butotnClick;
</script>

使用例

  <body>
    <p>ボタンをクリックしてください。</p>

    <input type="button" value="button" id="test" />

    <script>
      let button = document.getElementById("test");
      button.onclick = function (event) {
        console.log("eventのタイプ " + event.type);
        console.log("eventのターゲット " + event.target);
      };
    </script>
  </body>

// eventのタイプ click
// eventのターゲット [object HTMLInputElement]

addEventListenerメソッドを使ってイベントリスナーを登録

  • addEventListener メソッドを使用してイベントに対して登録した関数のことを特にイベントリスナーと呼ぶ
target.addEventListener(type, callback [, options])
  • 第一引数にはイベントの種類を表す文字列を指定(clickなどなど)
  • 第二引数にはイベントハンドラとしてコールバック関数を指定
  • 第さん引数にはオプション
<input type="button" value="button" id="test">

<script>
    function butotnClick(){
        console.log('お買い上げありがとうございます');
    }

    let button = document.getElementById('test');
    button.addEventListener('click', butotnClick);
</script>

idテストのボタンをユーザーがクリックしたあら、コールバック関数であるbutotnClickが呼び出されます。

  • 無名関数を使う場合は
<input type="button" value="button" id="test" />

<script>
  let button = document.getElementById("test");
  button.addEventListener("click", function () {
    console.log("テストします");
  });
</script>
  • アロー関数を使う場合
<input type="button" value="button" id="test" />

<script>
  let button = document.getElementById("test");
  button.addEventListener("click", () => {
    console.log("テストします");
  });
</script>
同じターゲットの同じイベントに対して複数のイベントリスナーを登録

HTML の属性値や DOM で取得した要素のプロパティに対してイベントハンドラを登録した場合、属性値やプロパティは一つの値しか保管できないため、同じイベントに対するイベントハンドラは一つしか登録することができない。

しかし、 addEventListener メソッドを使ってイベントリスナーを登録する場合には、同じターゲットの同じイベントに対して複数のイベントリスナーを登録することができる

ベントが発生すると登録されている順番に複数のイベントリスナーが呼び出される。

<input type="button" value="button" id="test" />

<script>
  function dispGreeting() {
    console.log("こんにちは");
  }

  function dispGoodNight() {
    console.log("こんばんは");
  }

  let button = document.getElementById("test");
  button.addEventListener("click", dispGreeting);
  button.addEventListener("click", dispGoodNight);
</script>

// こんにちは
// こんばんは
イベントリスナーを解除
  • removeEventListener メソッドは指定のイベントが発生したときに実行されるイベントリスナーを登録する
target.removeEventListener(type, callback [, options])
  • 削除するイベントリスナーを登録したときと同じ引数を指定する
  • 一致するイベントリスナーを削除
<input type="button" value="button" id="test" />

<script>
  function buttonClick() {
    console.log("こんにちは!これで最後です。");
    this.removeEventListener("click", buttonClick);
  }

  let button = document.getElementById("test");
  button.addEventListener("click", buttonClick);
</script>

一度だけイベントリスナーが呼びだされると、かんすないでイベントリスナーが解除される。

  • 無名関数を利用している場合第二引数に実行しちる関数を参照することができるarguments.calleeを指定する

発生したイベントの情報を取得する(Event)

イベントが発生して登録されたイベントハンドラやイベントリスナーが呼び出されると、一番目の引数に発生したイベントの情報が格納された Event オブジェクトが渡されてくる。

Eventオブジェクトの受け取り

ターゲットとなる要素のプロパティに対してイベントハンドラを登録する場合と、 addEventListener メソッドを使ってイベントリスナーを登録した場合、イベントが発生すると登録したコールバック関数が呼び出され、その時、第一引数に発生したイベントの情報が格納されたEventオブジェクトが渡される。

関数内でEventオブジェクトの情報を使いたい場合に活用できる。

<script>
    function butotnClick(event){
        console.log('こんにちは');
    }

    let button = document.getElementById('test');
    button.addEventListener('click', butotnClick);
</script>
Eventオブジェクトで取得できる情報

コールバック関数が呼び出されるときに引数として渡されてきた Event オブジェクトのプロパティを参照することで、発生したイベントに関する情報を取得できる。

プロパティ名 説明
Event.eventPhase イベントのフェーズ(キャプチャリングフェーズ、ターゲットフェース、バブリングフェーズ)
Event.type イベントの種類を表す文字列
Event.target イベントが発生した要素
Event.isTrusted イベントがユーザーの操作によって発生したかどうか
Event.bubbles イベントがバブリングするかどうか
Event.timeStamp 要素が作成されてからイベント発生までの経過時間(ミリ秒)
Event.cancelable イベントがキャンセル可能かどうか
Event.currentTarget イベントハンドラを登録した要素

以下のように使う

event.type

使用例

    <p>ボタンをクリックしてください。</p>

    <input type="button" value="button" id="xxx" />

    <script>
      let button = document.getElementById("xxx");

      button.addEventListener("click", function (event) {
        console.log("bubbles       :" + event.bubbles);
        console.log("cancelable    :" + event.cancelable);
        console.log("currentTarget :" + event.currentTarget);
        console.log("eventPhase    :" + event.eventPhase);
        console.log("target        :" + event.target);
        console.log("timeStamp     :" + event.timeStamp);
        console.log("type          :" + event.type);
        console.log("isTrusted     :" + event.isTrusted);
      });
    </script>


// bubbles       :true
// cancelable    :true
// currentTarget :[object HTMLInputElement]
// eventPhase    :2
// target        :[object HTMLInputElement]
// timeStamp     :1392.199999988079
// type          :click
// isTrusted     :true
Event.currentTargetプロパティとEvent.targetプロパティの違い
プロパティ名 説明
Event.currentTarget イベントハンドラを登録した要素
Event.target イベントが発生した要素

Event.curentTargetは以下のようなイベントハンドラを登録したターゲットの要素を指す。

let button = document.getElementById('xxx');
button.addEventListener('click', butotnClick);

Event.targetは実際にイベントが発生した要素を指します。

イベントに対するデフォルトの動作をキャンセルする(preventDefault)

リンクをクリックすれば、リンク先のページに移動したり、チェックボックスをクリックすればチェックが入るなどのデフォルトの動きが設定されているがpreventDefaultを使用するとキャンセルすることができる。

event.preventDefault()
  • すべてのイベントでキャンセルが可能ではない
  • Event.cancelableプロパティの値が true のイベントのみキャンセル可能
  <body>
    <div>
      <input type="checkbox" id="check1" />
      <label for="check1">はい</label>

      <input type="checkbox" id="check2" />
      <label for="check2">いいえ</label>
    </div>

    <script>
      let checkBox = document.getElementById("check2");

      checkBox.addEventListener("click", function (event) {
        alert("現在変更できません");
        event.preventDefault();
      });
    </script>
  </body>

「はい」にはチェックがいれられるが、「いいえ」をクリックするとアラートが出現してチェックが入らなくなる。

イベントをコードから発生させる(dispatchEvent)

イベントは HTTP ページをブラウザで見ているユーザーの操作によって発生するが、プログラムの中で新しいイベントを作成し指定した対象でイベントを発生させることがでできる。

  • 作成するには Event オブジェクトをのコンストラクタを使用
new Event(eventtype[, option])
  • 第一引数にイベントの種類を表す文字列を指定
  • カスタムイベントを作る場合は任意の名前
  • 第二引数には以下の3つの値に関する値をもったオブジェクトを指定する(省略可能)
    • bubbles(初期値は false): バブリングフェーズでイベントが伝搬されていくかどうか
    • cancelable(初期値は false): イベントがキャンセル可能かどうか
    • composed(初期値は false)
  • click イベントを新しく作成するには次のように記述
let e = Event('click');
イベントを発生させる
  • dispatchEvent メソッドは対象となるターゲットに引数に指定したイベントを発生させる
  • 引数には Event オブジェクトを指定
target.dispatchEvent(event)
  • click イベントを発生させるには次のように記述
let e = Event('click');

let target = document.getElementById('test');
target.dispatchEvent(e);
  • 発生したイベントがユーザーの操作によって発生したものか、それとも dispatchEvent メソッドによるコードから発生させられたものかは Event.isTrusted プロパティを参照することで判別できる
 <body>
    <div>
      <input type="button" id="btn1" value="button1" />
      <input type="button" id="btn2" value="button2" />
    </div>

    <script>
      let btn1 = document.getElementById("btn1");
      let btn2 = document.getElementById("btn2");

      btn1.addEventListener("click", function (event) {
        console.log("buttonA:" + event.isTrusted);

        let newEvent = new Event("click");
        btn2.dispatchEvent(newEvent);
      });

      btn2.addEventListener("click", function (event) {
        console.log("buttonB:" + event.isTrusted);
      });
    </script>
  </body>

// ボタン1を押した場合
  //buttonA:true
  //buttonB:false

// ボタン2を押した場合
  //buttonB:true
  • ボタン1が押された場合 まずユーザーのクリックにより表示されるのでbuttonA:trueが表示され、そのあとにクリックイベントを作成し、 dispatchEvent メソッドによってボタン2もユーザーの行動によらず押される。そのためボタンBのisTrustedではfalseとなっている。