概要
イベント駆動は「クリックを検知する」だけの仕組みではない。
それは**“状態の変化を検出し、依存関係を分離しながら情報を伝播させるための非同期通知機構”**である。
JavaScriptでは、DOMイベントだけでなく、カスタムイベント・Pub/Sub(Publish/Subscribe)・イベントバスなどを活用した抽象化された通知設計が求められる場面が増えている。
本稿では、イベントの構造設計、DOMイベントとの違い、カスタムイベントの使い方、通知機構の抽象化戦略を整理して解説する。
1. DOMイベントの基本
document.querySelector('#btn')?.addEventListener('click', () => {
console.log('Clicked');
});
- ✅ DOMに直接結びつく「UIに限定されたイベント」
- ✅ バブリング/キャプチャ/イベント委譲 などの概念と強く関係
2. カスタムイベントの基本構文
const event = new CustomEvent('user:login', {
detail: { userId: 42 }
});
document.dispatchEvent(event);
document.addEventListener('user:login', (e) => {
console.log('Login detected:', e.detail.userId);
});
- ✅
CustomEvent
を用いてアプリケーション内部で意味のあるイベントを定義可能 - ✅
detail
に構造化されたデータを付加できる
3. イベントによる依存関係の分離
// producer.js
function login(userId) {
document.dispatchEvent(new CustomEvent('user:login', { detail: { userId } }));
}
// consumer.js
document.addEventListener('user:login', (e) => {
updateUI(e.detail.userId);
});
- ✅ 発生源(producer)と反応側(consumer)を直接結合せずに通信可能
- ✅ 疎結合な構造が、メンテナンス性と拡張性を向上させる
4. イベントバス(Event Bus)による抽象化
const EventBus = new EventTarget();
// 発火
EventBus.dispatchEvent(new CustomEvent('item:added', { detail: { id: 99 } }));
// 監視
EventBus.addEventListener('item:added', (e) => {
console.log('Item added:', e.detail.id);
});
- ✅ グローバルな状態管理とは分離された通知ハブとして機能
- ✅ 状態変化・ユーザー操作・外部イベントなどを統合的に受け渡し可能
5. Pub/Subパターンの抽象化例
class PubSub {
constructor() {
this.subscribers = {};
}
subscribe(topic, callback) {
if (!this.subscribers[topic]) this.subscribers[topic] = [];
this.subscribers[topic].push(callback);
}
publish(topic, data) {
(this.subscribers[topic] || []).forEach(cb => cb(data));
}
}
const bus = new PubSub();
bus.subscribe('notify', (msg) => console.log('Received:', msg));
bus.publish('notify', 'Hello!');
- ✅ シンプルでテスト可能なイベント通知抽象化
- ✅ 通信の方向を一方向に限定できる → 管理しやすい設計
設計判断フロー
① UIイベントと内部通知が混在していないか? → CustomEventで明確化
② ロジックと反応側が密結合していないか? → イベント抽象化で分離
③ 状態変化の通知はどこから来ているか? → EventBus/PubSubで一元化
④ モジュール間通信が直接参照になっていないか? → イベント経由に切り替える
⑤ コンポーネントの独立性は保たれているか? → イベントは発火/反応で責務分離
よくあるミスと対策
❌ カスタムイベントを使わず、関数呼び出しでロジックが依存
→ ✅ イベント通知で疎結合に設計する
❌ UIイベントと内部ロジック通知が混在して読みにくい
→ ✅ data-action
によるセレクタと、user:*
のようなイベント名空間で整理
❌ window
や document
に直接バインドしすぎてテスト困難
→ ✅ EventBusや抽象化PubSubクラスを用意して差し替え可能に
結語
イベント設計とは「クリックを処理するもの」ではない。
それは**“状態の変化と責務の連鎖を、構造として分離・通知するための非同期制御構造”**である。
- DOMを超えて通知を構造化する
- UI層とロジック層を非同期で接続する
- 発火と反応を明確に分け、責任範囲を定義する
JavaScriptにおけるイベント設計とは、
“変化の伝播を構造として抽象化することで、モジュールの独立性を保ち続ける戦略である。”