吉本集の個人ブログ
Web制作の技術について書いています。たまに日記も書きます。

【JS入門】イベントについて Vol.3(イベントハンドラプロパティ、イベントの削除、カスタムイベントの作成)

2018年2月11日 / category : javascript

イベントハンドラプロパティについて

メソッドaddEventListenerでイベントハンドラを登録することでイベントを設定できると解説してきましたが、もう一つの方法として、イベントハンドラプロパティを使用することでイベントを設定することができます。イベントハンドラプロパティの名前は基本的には次のような命名規則になっています。

on + イベントタイプ = 関数

例えば、簡単な例としてクリックイベントを設定する場合は、次のようなコードとなります。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<button id="btn">ボタン</button>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let btn = document.getElementById('btn');
btn.onclick = function(){
	console.log('お腹減った。');
}

コンソール画面(ボタンクリック後)

お腹減った。

上の例は、onclickプロパティに無名関数を代入していますが、名前付きの関数を代入すると次のようになります。

sample.js
let btn = document.getElementById('btn');

function debug(){
	console.log('お腹減った。');
}

btn.onclick = debug;

結果は先ほどのコードと変わりません。メソッドaddEventListenerでイベントを設定する方法との違いは、イベントハンドラプロパティでのイベント設定の場合、複数のイベントを設定することができません。例えば、次のようなコードを見てみます。

sample.js
let btn = document.getElementById('btn');

function debug(){
	console.log('お腹減った。');
}

function debug2(){
	console.log('お腹減ってない。');
}

btn.onclick = debug;
btn.onclick = debug2;

コンソール画面(ボタンクリック後)

お腹減ってない。

イベントハンドラプロパティonclickに関数debug、関数debug2をそれぞれ代入しましたが、これはプロパティの値の上書きになってしまうため関数debug2しか実行されません。
その点、メソッドaddEventListenerは複数のイベントハンドラを登録することができますので、メリットと言えます。イベントの設定に関しては、メソッドaddEventListenerを使用することを推奨します。

イベントの削除

メソッドaddEventListenerを使用してイベントを設定した後に、イベントを削除したい場合が出てきます。理想的なプログラムは不要になったイベントは削除すべきです。
イベントを削除する場合は、メソッドremoveEventListenerを使用します。

EventTarget.removeEventListener(eventType, listener, useCapture):イベントターゲットにイベントハンドラ(イベントリスナー)を登録する。
引数 eventType(string) : イベントタイプ
引数 listener(function) : イベントハンドラ(イベントリスナー)
引数 useCapture(boolean) : キャプチャフェーズの設定
構文

EventTarget.emoveEventListener('click', open, false);

sample.js
let btn = document.getElementById('btn');
btn.onclick = function(){
	console.log('お腹減った。');
}
sample.js
let btn = document.getElementById('btn');

function debug(){
	btn.removeEventListener('click', debug);
	console.log('お腹減った。');
}

btn.addEventListener('click', debug);

関数debugの中でメソッドremoveEventListenerを設定しています。ボタンを一度クリックすると、メソッドremoveEventListenerが実行されイベントが削除されますので、ボタンクリック2回目以降は、関数debugは実行されません。
イベントハンドラプロパティでイベントを削除したい場合は、プロパティの値にnullを代入することでイベントを削除することができます。

sample.js
let btn = document.getElementById('btn');

function debug(){
	btn.onclick = null;
	console.log('お腹減った。');
}

btn.onclick = debug;

カスタムイベントの作成

今まで解説してきたclickなどのイベントタイプは既に用意されているイベントです。イベントはイベントオブジェクトを生成することで独自に作ることができます。イベントオブジェクトを生成するには、Eventインターフェースが定義しているコンストラクタ関数を使用して生成します。

let event = new Event('イベント名');

clickイベントは、イベントターゲットをクリックすることでイベントが発生(発火)しますが、独自で生成したイベントには発生(発火)するきっかけがありません。このきっかけを実行するには、メソッドdispatchEventを使用します。

EventTarget.dispatchEvent(event):イベントリスナーを呼び出す(実行する)
引数 event(Event) : 実行するイベントオブジェクト
構文

target.dispatchEvent(event);

sample.js
let body = document.body;
let event = new Event('customEvent');

function debug(){
	console.log('お腹減った。');
}

body.addEventListener('customEvent', debug);
body.dispatchEvent(event);

コンソール画面

お腹減った。

上のコードを見るとbody要素に対しイベントハンドラを登録しています。イベントタイプには独自で作成したイベントオブジェクトのイベントタイプを設定しています。イベントが発生(発火)するタイミングは、イベントハンドラを登録してすぐにメソッドdispatchEventを実行していますので、ブラウザを確認するとコンソールに「お腹減った。」が表示されます。
ただし、イベントオブジェクトをコンストラクタ関数で生成する方法は、IEでは対応できていません。IEに対応させるためには、別の方法が必要です。
Documentインターフェースで定義されているメソッドcreateEventでイベントオブジェクトを生成し、生成されたEventオブジェクトのメソッドinitEventでイベントを初期化することで、メソッドdispatchEventに渡すことができます。

Document.createEvent(eventType):イベントオブジェクトを生成する
引数 eventType(string) : イベントタイプ名
戻り値(return): Eventオブジェクト(Event)
構文

let event = document.createEvent('Event');

メソッドcreateEventの引数で設定するイベントタイプの文字列は、DOM Events 仕様書などのイベントモジュールで定義されています。
今回使用するコードでは、文字列「Event」を使用します。これにより初期化する際に実行するメソッドinitEventを使用することができます。

Event.initEvent(eventType,bubbles,cancelable):イベントオブジェクトを初期化する
引数 eventType(string) : イベントタイプ名
引数 bubbles(boolean) : イベント伝播(イベントバブリング)をさせるかの真偽値
引数 cancelable(boolean) : イベントがキャンセル可能かどうかを設定する真偽値
構文

event.initEvent('customEvent', true, true);

sample.js
let body = document.body;
let event = document.createEvent('Event');
event.initEvent('customEvent', true, true);

function debug(){
	console.log('お腹減った。');
}

body.addEventListener('customEvent', debug);
body.dispatchEvent(event);

コンソール画面

お腹減った。

上のコードがIEにも対応したイベントオブジェクトの生成です。ここでイベント伝播(イベントバブリング)について触れてみます。IEに対応させた上のコードでは、メソッドinitEventを実行する際、第2引数の値をtrueにすることでイベント伝播(イベントバブリング)を設定することができます。次のコードを見てみます。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>タイトル</title>
</head>
<body>
<div id="inner">
<button id="btn">ボタン</button>
</div>
<script src="./js/sample.js"></script>
</body>
</html>
sample.js
let body = document.body;
let inner = document.getElementById('inner');
let btn = document.getElementById('btn');
let event = document.createEvent('Event');
event.initEvent('customEvent', true, true);

function debug(e){
	console.log('this : ', this);
}
body.addEventListener('customEvent', debug);
inner.addEventListener('customEvent', debug);

btn.addEventListener('click', function(){
	inner.dispatchEvent(event);
});

コンソール画面(ボタンクリック後)

this : <div id=​"inner">...</div>
this : <body>...</body>

ボタンをクリックすると、div要素に設定したイベントハンドラが発生(発火)します。注目したいのはbody要素に設定したイベントハンドラも発生(発火)している点です。
これは生成したイベントオブジェクトをメソッドinitEventで初期化する際、第二引数の値をtrueにすることでイベント伝播(イベントバブリング)の設定をしているため、div要素と同じイベントタイプを設定したbody要素のイベントハンドラも発生(発火)します。これを先ほどのコンストラクタ関数でイベントオブジェクトを生成する方法で確認してみます。

sample.js
let body = document.body;
let inner = document.getElementById('inner');
let btn = document.getElementById('btn');
let event = new Event('customEvent');

function debug(e){
	console.log('this : ', this);
}
body.addEventListener('customEvent', debug);
inner.addEventListener('customEvent', debug);

btn.addEventListener('click', function(){
	inner.dispatchEvent(event);
});

コンソール画面(ボタンクリック後)

this : <div id=​"inner">...</div>

上の結果から、コンストラクタ関数でのイベントオブジェクトの生成では、イベント伝播(イベントバブリング)が起きません。もしイベント伝播(イベントバブリング)を設定するのであれば、こちらもメソッドinitEventを実行する必要があります。

sample.js
let body = document.body;
let inner = document.getElementById('inner');
let btn = document.getElementById('btn');
let event = new Event('customEvent');
event.initEvent('customEvent', true, true);

function debug(e){
	console.log('this : ', this);
}
body.addEventListener('customEvent', debug);
inner.addEventListener('customEvent', debug);

btn.addEventListener('click', function(){
	inner.dispatchEvent(event);
});

コンソール画面(ボタンクリック後)

this : <div id=​"inner">...</div>
this : <body>...</body>

上のコードでイベント伝播(イベントバブリング)がうまく動作していることが確認できました。これまではIEに対応する方法と別々のコードにしていましたが、次のようにtry-cach構文を使用することでまとめることができます。これが最終的な記述方法になります。

sample.js
let body = document.body;
let inner = document.getElementById('inner');
let btn = document.getElementById('btn');

var customEvent;
const EVENT_TYPE = 'customEvent';
try{
	customEvent = new Event(EVENT_TYPE);
}catch(e){
	customEvent = document.createEvent('Event');
};
customEvent.initEvent(EVENT_TYPE, true, true);

function debug(e){
	console.log('this : ', this);
}
body.addEventListener(EVENT_TYPE, debug);
inner.addEventListener(EVENT_TYPE, debug);

btn.addEventListener('click', function(){
	inner.dispatchEvent(customEvent);
});