javascript：カスタムイベントを使ったイベントハンドラの実装

# 動機、やること

- javascriptに（いつからか）[CustomEvent](https://developer.mozilla.org/ja/docs/Web/API/CustomEvent)というユーザ定義のイベント発行機能が存在する（していた）ことを知った
- オブジェクト間のメッセージング処理に使いたい
- 恒例のIE独自仕様やイベントオブジェクトのdetailプロパティにカスタムデータを渡す仕様などは、僕の脳内の希少なワークスペースからご退出いただきたい
- => CustomEventをラッパしたイベント処理を実装しよう！

# 開発環境

前提として環境は以下のような感じ。

- npm
- webpack3
- babel
- es6っぽく

# 実装(ライブラリ)

以下の実装全体を[github](https://github.com/tbtbt/js-customevent-sample)にアップしました。差分がありましたらgithubを正とさせてください。。

イベント処理の実装本体はこんな感じ。

```event.js
/**
 * 指定したkey名のカスタムイベントを送信する
 * @method send
 * @param  {string} key   カスタムイベント名
 * @param  {object} value データ
 */
export const dispatch = (key, value) => {
	let ev;
	try {
		ev = new CustomEvent(key, {detail: value});
	} catch(e) { // for IE
		ev = document.createEvent('CustomEvent');
		ev.initCustomEvent(key, false, false, value);
	}
	document.getElementsByTagName('body')[0].dispatchEvent(ev);
};

/**
 * カスタムイベントのリスナーを登録する
 * @method listen
 * @param  {string}   key      カスタムイベント名
 * @param  {Function} callback イベントリスナーから呼ばれる関数
 */
export const listen = (key, callback) => {
	document.getElementsByTagName('body')[0].addEventListener(key, callback);
};
```

今回はクラス間のイベント送受信が目的だったため、HTML上で確実に存在するbodyタグを固定で使用してイベント発行を行っています。

また、IEだけCustomEventコンストラクタが使えない独自仕様なので、IEの実装を吸収してあげます。
＃こちら、出典サイトを忘れてしまったので(qiita内だったような)、ご存知の方は名乗り上げていただければ幸いです（他薦も可）。

## 実装(イベントリスナ側)

イベントリスナ側の実装は以下のような感じ。

```listener.js
import { listen } from './event.js';
listen('app:ping', (e) => alert(`ping - ${e.detail}`));
```

## 実装(イベント発行側)

イベント発行側はこんな感じ。
ボタンのclickイベントでカスタムイベントを発行します。

```dispatcher.js
import { dispatch } from './event.js';
document.getElementByTagName('button')[0].onclick = () =>  dispatch('app:ping', 'pong');
```

## 実装(その他)

ビルドのエントリポイントになるapp.jsを作成します

```app.js
require('./listener.js');
require('./dispatcher.js');
```

ついでにwebpack.config.js

```webpack.config.js
const path = require('path');
const webpack = require('webpack');

module.exports = {
	entry: {
		'app': './app.js'
	},
	output: {
		path: path.resolve(__dirname, "./"),
		filename: 'static/js/[name].js',
	},
	module: {
		rules: [
			{
				test: /\.js$/,
				exclude: [/node_modules/],
				use:[{
					loader: "babel-loader",
					options:{
						presets:[
							['env', {'modules': false}]
						]
					}
				}]
			},
		]
	},
	devServer: {
		contentBase: path.resolve(__dirname, "./"),
		port:3000,
	},
	devtool: 'source-map',
};
```

package.jsonを適当に書いておきます。
`npm init`で生成し、依存関係を追加するなどしておいてください。
今回はbabelとwebpack、およびwebpack-dev-serverを使います。

```package.json
{
    "name": "js-customevent",
    "version": "0.0.1",
    "description": "CustomEvent wrapper",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "start": "node_modules/.bin/webpack-dev-server"
    },
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "babel-core": "^6.26.0",
        "babel-loader": "^7.1.2",
        "babel-preset-env": "^1.6.1",
        "webpack": "^3.10.0",
        "webpack-dev-server": "^2.11.1"
    }
}
```

HTMLは最低限こんな感じ。

```index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
</head>
<body>
    <button type="button">ボタン</button>
</body>
<script src="/static/js/app.js"></script>
</html>
```

上記ファイルをすべて同じフォルダに格納してください。


# 動作確認

上記準備が整った後で、ルートディレクトリ下で以下を実行します。

```
$ npm install
$ npm start
```

ブラウザから、[http://localhost:3000/]() にアクセスしてください。
ボタンを押して、以下のようなalertが表示したら動作確認完了です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/127179/9e2d0051-888e-f418-b6b5-d3393ce252e8.png)

dispatch.js側で渡した「pong」というメッセージがlistener.jsで実行したイベントリスナに捕捉されて、alertに反映しました。

# まとめ

javascriptのCustomEventを使ったイベントハンドリングについてご紹介しました。ご参考になれば幸いです。

こちらの現場からは以上です。