動機、やること
- javascriptに(いつからか)CustomEventというユーザ定義のイベント発行機能が存在する(していた)ことを知った
- オブジェクト間のメッセージング処理に使いたい
- 恒例のIE独自仕様やイベントオブジェクトのdetailプロパティにカスタムデータを渡す仕様などは、僕の脳内の希少なワークスペースからご退出いただきたい
- => CustomEventをラッパしたイベント処理を実装しよう!
開発環境
前提として環境は以下のような感じ。
- npm
- webpack3
- babel
- es6っぽく
実装(ライブラリ)
以下の実装全体をgithubにアップしました。差分がありましたらgithubを正とさせてください。。
イベント処理の実装本体はこんな感じ。
/**
* 指定した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内だったような)、ご存知の方は名乗り上げていただければ幸いです(他薦も可)。
実装(イベントリスナ側)
イベントリスナ側の実装は以下のような感じ。
import { listen } from './event.js';
listen('app:ping', (e) => alert(`ping - ${e.detail}`));
実装(イベント発行側)
イベント発行側はこんな感じ。
ボタンのclickイベントでカスタムイベントを発行します。
import { dispatch } from './event.js';
document.getElementByTagName('button')[0].onclick = () => dispatch('app:ping', 'pong');
実装(その他)
ビルドのエントリポイントになるapp.jsを作成します
require('./listener.js');
require('./dispatcher.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を使います。
{
"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は最低限こんな感じ。
<!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が表示したら動作確認完了です。
dispatch.js側で渡した「pong」というメッセージがlistener.jsで実行したイベントリスナに捕捉されて、alertに反映しました。
まとめ
javascriptのCustomEventを使ったイベントハンドリングについてご紹介しました。ご参考になれば幸いです。
こちらの現場からは以上です。