【jQuery】イベントの二重登録を防ぐ!名前空間(Namespace)を活用した安全なイベント実装
JavaScript(jQuery)で画面の非同期更新(Ajax)などを実装していると、**「ボタンを1回しか押していないのに、なぜかイベントが2回、3回と走ってしまう…」**というバグに遭遇することがあります。
今回は、この「多重登録」が起きるメカニズムと、それをスマートに解決する .off().on() と**名前空間(Namespace)**を組み合わせた安全な実装パターンを解説します。
1. 多重登録とは何か
例えば、普通は以下のようにイベントを設定(初期化)するコードを書くとします。
$('#js-search-btn').on('click', function () {
_executeSearch();
});
これが画面の読み込み時に1回だけ実行されるなら何も問題ありません。
しかし、Single Page Application(SPA)のような画面遷移のない構成や、Ajaxによる部分的な画面書き換え、または特定のJSファイルの重複読み込みなど、何らかの理由でこのJS初期化処理が2回、3回と実行されてしまうと、同じボタンに対してクリックイベントが複数登録(多重登録)されてしまいます。
初期化が3回走ってしまった場合のイメージ
// 1回目の初期化
$('#js-search-btn').on('click', function () {
_executeSearch();
});
// 2回目の初期化(イベントが上書きされず、追加されてしまう)
$('#js-search-btn').on('click', function () {
_executeSearch();
});
// 3回目の初期化(さらに追加されてしまう)
$('#js-search-btn').on('click', function () {
_executeSearch();
});
この(イベントが3つ重なった)状態で検索ボタンを1回クリックすると、内部では以下のような挙動になります。
検索ボタンを「クリック1回」
↓
_executeSearch() 実行(1回目)
_executeSearch() 実行(2回目)
_executeSearch() 実行(3回目)
つまり、検索ボタンを1回押しただけなのに、裏ではAjaxの通信が3回も飛ぶということになってしまいます。サーバーへの無駄な負荷になりますし、予期せぬバグの原因にもなります。
2. 解決策:名前空間を使った .off().on() パターン
この重複を防ぐためのベストプラクティスが、「新しくイベントを登録する直前に、特定のイベントだけを一度解除する」という方法です。
$(document)
// 1. 既存の「独自の検索イベント」だけをピンポイントで解除
.off('click.searchApp', '#js-search-btn')
// 2. 改めてイベントを1つだけクリーンに登録
.on('click.searchApp', '#js-search-btn', function (event) {
_executeSearch();
});
コードのポイント
① 名前空間( .searchApp )の付与
click の後ろに .searchApp という任意の文字列をくっつけています。これが名前空間(Namespace)です。
これをしておくことで、.off('click.searchApp') を実行した際に、他のプラグインや別処理で設定された無関係なクリックイベントを巻き添えにせず、この検索処理用のクリックイベントだけをピンポイントで削除できます。
② イベントデリゲーション( $(document) )の採用
第2引数にセレクタ( #js-search-btn )を指定することで、ドキュメント全体でイベントを監視しています。これにより、Ajaxなどで後から動的に追加(生成)されたボタンに対しても、自動的にイベントが適用されます。
これにより、初期化処理が何回走ろうとも「直前のイベントを消して、常に1つだけ登録する」という状態が保たれるため、多重登録バグを完璧に防ぐことができます。
まとめ
「イベントがなぜか多重に動いてしまう」という現象は、フロントエンド開発では非常によくあるバグの1つです。
jQueryでイベントを登録する際は、後々の画面拡張やパーツの非同期化を見据えて、最初から名前空間をセットにした .off().on() パターンで書いておくのが安全でおすすめです!
因みに ⇒ バインドの基本的な考え方について
| 状況 | 書き方 |
|---|---|
要素は最初からHTMLに存在していて、単に show() / hide() するだけ |
通常の $('#id').on(...) でも問題なし |
| 要素をAjaxなどで後から生成する、またはHTMLを差し替える |
$(document).on(...) のイベント委譲が安全 |
| モーダルを何度も開いてイベント登録処理が再実行される |
off/on + 名前空間が安全 |