- Modalの多重表示を防ぐユーティリティは「画面を“ごちゃごちゃさせない”ための安全装置」です
- Modalの多重表示が起きる典型パターンをイメージする
- ステップ1:「今開いているModal」を管理するという発想を持つ
- ステップ2:ModalControllerに「状態管理」を追加する
- ステップ3:多重表示を「完全に禁止する」パターン
- ステップ4:「今のModalを閉じてから次を開く」パターン
- ステップ5:ボタン連打対策として「開いている間はトリガーを無効化する」
- ステップ6:複数種類のModalを扱うときの設計(スタックを使うかどうか)
- ステップ7:「多重表示を防ぐ」ことと「エラー表示をきちんと出す」ことのバランス
- ステップ8:業務で使える「Modal多重表示防止ユーティリティ」まとめ
- ステップ9:絶対に避けるべき「多重表示の危険なパターン」を明確にしておく
- まとめ:Modalの多重表示を防ぐユーティリティは「画面を整然と保ち、ユーザーの集中を守るための実務的な技術」です
Modalの多重表示を防ぐユーティリティは「画面を“ごちゃごちゃさせない”ための安全装置」です
業務システムでModal(モーダルダイアログ)を使っていると、 次のような状況に遭遇することがあります。
- ユーザーがボタンを連打して、同じModalが何度も開いてしまう
- 非同期処理の結果で、短時間に複数のエラーモーダルが重なって表示される
- 別のModalが開いているのに、さらに別のModalを開いてしまい、画面が混乱する
こうした 「Modalの多重表示」 は、
- ユーザーの視線と操作の対象が分散してしまう
- どのModalを閉じればよいか分からなくなる
- バグや誤操作の原因になる
という意味で、業務システムでは避けたい挙動です。
そこで役に立つのが、 「Modalの多重表示を防ぐためのJavaScriptユーティリティ」です。
ここでは、初心者向けに、
- なぜ多重表示が問題になるのか
- どのように「今開いているModal」を管理するか
- 実務で使えるユーティリティと設計
をステップバイステップで解説していきます。
Modalの多重表示が起きる典型パターンをイメージする
まず、「どんなときに多重表示が起きるのか」を具体的にイメージしてみます。
パターン1:ボタン連打による同じModalの多重表示
button.addEventListener("click", () => {
ModalController.open("my-modal");
});
JavaScriptユーザーがこのボタンを素早く連打すると、
- CSSやアニメーションのタイミングによっては、 「開く処理」が何度も走る
- DOMを毎回生成している場合は、同じModalが複数重なってしまう
という状態になりがちです。
パターン2:非同期処理の結果で複数Modalが重なる
fetch("/api/action")
.then((res) => {
if (!res.ok) {
ModalController.open("error-modal");
}
});
fetch("/api/another-action")
.then((res) => {
if (!res.ok) {
ModalController.open("error-modal");
}
});
JavaScript短時間に複数のエラーが発生すると、
- 同じエラーモーダルが何度も開こうとする
- 別のエラーモーダルが重なって表示される
という状態になります。
パターン3:別のModalが開いているのにさらに別のModalを開く
- 設定Modalを開いている状態で、 その中からさらに確認Modalを開く
- どちらも閉じられるが、ユーザーは「どこから来たのか」を見失いやすい
ポイント:Modalの多重表示は「ユーザーの集中を一点に集める」というModal本来の役割を損ない、操作性と信頼性を下げてしまいます。
ステップ1:「今開いているModal」を管理するという発想を持つ
Modalの多重表示を防ぐための基本的な考え方は、
「今開いているModalを1つだけに制限し、 新しく開く前に状態を確認する」
というものです。
管理したい情報
- 現在開いているModalのID
- 例:
"my-modal"、"error-modal"など
- 例:
- 開いているかどうかのフラグ
- 例:
isAnyModalOpen = true/false
- 例:
これをユーティリティの中で管理することで、
- すでにModalが開いているときは、新しいModalを開かない
- 必要なら「今のModalを閉じてから次を開く」
といった制御が可能になります。
ステップ2:ModalControllerに「状態管理」を追加する
前回の「開く/閉じる」ユーティリティに、 「今開いているModal」を管理する機能を追加していきます。
基本となるModalControllerの拡張
export const ModalController = {
_currentModalId: null, // 今開いているModalのID(なければ null)
isOpen() {
return this._currentModalId !== null;
},
getCurrentModalId() {
return this._currentModalId;
},
open(modalId) {
// すでに何か開いている場合は、ここで制御する
if (this.isOpen()) {
// 多重表示を防ぐため、何もしないパターン
// または、現在のModalを閉じてから開くパターンを選べます
console.warn("Modal is already open:", this._currentModalId);
return;
}
const el = document.getElementById(modalId);
if (!el) return;
el.classList.add("is-open");
el.setAttribute("aria-hidden", "false");
this._currentModalId = modalId;
},
close(modalId) {
const el = document.getElementById(modalId);
if (!el) return;
el.classList.remove("is-open");
el.setAttribute("aria-hidden", "true");
// 現在のModalIDをクリア
if (this._currentModalId === modalId) {
this._currentModalId = null;
}
},
closeCurrent() {
if (!this.isOpen()) return;
this.close(this._currentModalId);
},
};
JavaScriptここでの重要ポイント
_currentModalIdという内部変数で「今開いているModal」を記録しています。openの中でisOpen()をチェックし、 すでに開いている場合は「何もしない」か「閉じてから開く」かを選べます。closeの中で、閉じたModalが現在のものなら_currentModalIdをnullに戻します。
これにより、「今開いているModalは常に1つだけ」という状態を保ちやすくなります。
ステップ3:多重表示を「完全に禁止する」パターン
まずは一番シンプルな方針として、
「何かModalが開いているときは、新しいModalを開かない」
というルールを実装してみます。
open の中で何もしないパターン
open(modalId) {
if (this.isOpen()) {
console.warn("Modal is already open:", this._currentModalId);
return;
}
// 以下は前述のとおり
}
JavaScript使用例
button.addEventListener("click", () => {
ModalController.open("my-modal");
});
JavaScriptこの場合、
- ユーザーがボタンを連打しても、最初の1回だけModalが開く
- すでにModalが開いている状態で別のボタンを押しても、新しいModalは開かない
という挙動になります。
ポイント:業務システムでは「Modalは常に1つだけ」という設計にすることで、 画面の混乱を防ぎやすくなります。
ステップ4:「今のModalを閉じてから次を開く」パターン
場合によっては、
「今のModalを閉じてから、次のModalを開きたい」
というニーズもあります。
例えば、
- エラーモーダルを閉じたあとに、確認モーダルを開く
- 設定Modalから、別の詳細Modalに切り替える
などです。
切り替え用のメソッドを用意する
export const ModalController = {
// 既存のプロパティ・メソッドに加えて
switchTo(modalId) {
if (this.isOpen()) {
this.closeCurrent();
}
this.open(modalId);
},
};
JavaScript使用例
// ある処理の結果に応じて、Modalを切り替える
if (hasError) {
ModalController.switchTo("error-modal");
} else {
ModalController.switchTo("success-modal");
}
JavaScriptこのように switchTo を用意しておくと、
- 「今のModalを閉じてから次を開く」という流れを 1行で表現できるようになります。
ステップ5:ボタン連打対策として「開いている間はトリガーを無効化する」
Modalの多重表示は、 「開いている間にトリガーボタンが何度も押される」ことで起きることも多いです。
そこで、
「Modalが開いている間は、トリガーボタンを一時的に無効化する」
という対策も有効です。
トリガーボタンを無効化する例
function setupModalTriggers() {
document.querySelectorAll("[data-modal-open]").forEach((btn) => {
const modalId = btn.getAttribute("data-modal-open");
btn.addEventListener("click", () => {
if (ModalController.isOpen()) {
return; // すでに何か開いているなら何もしない
}
// ボタンを一時的に無効化
btn.disabled = true;
ModalController.open(modalId);
// Modalが閉じられたときにボタンを再度有効化する
const observer = new MutationObserver(() => {
if (!ModalController.isOpen()) {
btn.disabled = false;
observer.disconnect();
}
});
const backdrop = document.getElementById(modalId);
if (backdrop) {
observer.observe(backdrop, { attributes: true, attributeFilter: ["class"] });
}
});
});
}
JavaScriptこの例は少し高度ですが、 シンプルに「開いている間はボタンを無効化する」だけでも効果があります。
ポイント:ユーザーの連打による多重表示を防ぐには、「開いている間はトリガーを効かせない」という発想が有効です。
ステップ6:複数種類のModalを扱うときの設計(スタックを使うかどうか)
業務システムによっては、
- 「親Modalの上に子Modalを重ねる」
- 「確認Modalをさらに重ねる」
といった 「Modalのネスト」 を許容したい場合もあります。
このときの選択肢は2つです。
- スタック方式で管理する
- 開いているModalを配列(スタック)で管理し、 一番上のModalだけを操作する。
- ネストを禁止し、常に1つだけに制限する
- 前述のように
_currentModalIdだけを管理する。
- 前述のように
初心者向け・業務システム向けには、 「ネストを禁止して常に1つだけ」 の方がシンプルで安全なことが多いです。
ここでは、ネストを禁止する前提で話を続けます。
ステップ7:「多重表示を防ぐ」ことと「エラー表示をきちんと出す」ことのバランス
Modalの多重表示を防ぐときに注意したいのは、
「エラーや重要なメッセージが、ユーザーに届かなくなっていないか?」
という点です。
よくある失敗例
- すでにエラーモーダルが開いている状態で、 別のエラーが発生しても、新しいModalを開かないようにした結果、 後から発生したエラーがユーザーに伝わらない。
対策の考え方
- 「エラーが連続して発生した場合は、 1つのエラーモーダルの中にメッセージを追記する」
- 「新しいエラーが来たら、既存のエラーモーダルの内容を更新する」
例:エラーモーダルの内容を更新する
function showErrorModal(message) {
const modalId = "error-modal";
const backdrop = document.getElementById(modalId);
if (!backdrop) return;
const body = backdrop.querySelector(".modal-body");
if (body) {
body.textContent = message;
}
if (!ModalController.isOpen()) {
ModalController.open(modalId);
}
}
JavaScriptこのようにしておけば、
- すでにエラーモーダルが開いている場合は、 内容だけ更新して多重表示を防ぐ
- 開いていない場合は、新しく開く
という挙動になります。
ポイント:多重表示を防ぐときは、「メッセージがユーザーに届くこと」を同時に意識する必要があります。
ステップ8:業務で使える「Modal多重表示防止ユーティリティ」まとめ
ここまでの内容を、ひとつのユーティリティとして整理してみます。
export const ModalController = {
_currentModalId: null,
isOpen() {
return this._currentModalId !== null;
},
getCurrentModalId() {
return this._currentModalId;
},
open(modalId) {
if (this.isOpen()) {
console.warn("Modal is already open:", this._currentModalId);
return;
}
const el = document.getElementById(modalId);
if (!el) return;
el.classList.add("is-open");
el.setAttribute("aria-hidden", "false");
this._currentModalId = modalId;
},
close(modalId) {
const el = document.getElementById(modalId);
if (!el) return;
el.classList.remove("is-open");
el.setAttribute("aria-hidden", "true");
if (this._currentModalId === modalId) {
this._currentModalId = null;
}
},
closeCurrent() {
if (!this.isOpen()) return;
this.close(this._currentModalId);
},
switchTo(modalId) {
if (this.isOpen()) {
this.closeCurrent();
}
this.open(modalId);
},
};
export function showErrorModal(message) {
const modalId = "error-modal";
const backdrop = document.getElementById(modalId);
if (!backdrop) return;
const body = backdrop.querySelector(".modal-body");
if (body) {
body.textContent = message;
}
if (!ModalController.isOpen()) {
ModalController.open(modalId);
}
}
JavaScript使用例
// 通常のModal
document.querySelectorAll("[data-modal-open]").forEach((btn) => {
const modalId = btn.getAttribute("data-modal-open");
btn.addEventListener("click", () => {
ModalController.open(modalId);
});
});
// エラー表示
fetch("/api/action")
.then((res) => {
if (!res.ok) {
showErrorModal("処理に失敗しました。時間をおいて再度お試しください。");
}
});
JavaScriptステップ9:絶対に避けるべき「多重表示の危険なパターン」を明確にしておく
1. DOMを毎回生成してModalを増殖させる
button.addEventListener("click", () => {
document.body.innerHTML += `
<div class="modal-backdrop is-open">...</div>
`;
});
JavaScriptこれは、
- 同じModalが何度もDOMに追加される
- イベントハンドラが増殖する
- メモリリークや予期しない挙動の原因になる
ため、業務システムでは避けるべきです。
2. 「今開いているModal」を一切管理しない
- どのModalが開いているか分からない
- 閉じる処理が特定のModalだけを対象にしている
これは、バグの温床になります。
3. エラーをすべて別Modalで出し続ける
- エラーが連続すると、画面がエラーモーダルだらけになる
- ユーザーがどれを閉じればよいか分からなくなる
結論:Modalの多重表示を防ぐことは、「画面の秩序」を守るための重要な設計です。
まとめ:Modalの多重表示を防ぐユーティリティは「画面を整然と保ち、ユーザーの集中を守るための実務的な技術」です
Modalの多重表示を防ぐためのJavaScriptユーティリティと設計を導入することで、次のような効果があります。
- 常に「今開いているModalは1つだけ」という状態を保てる
- ボタン連打や連続エラーによる画面の混乱を防げる
- エラーメッセージや確認ダイアログを、ユーザーに分かりやすく提示できる
- チーム全体で「Modalの安全で整然とした使い方」を共有しやすくなる
業務システムにおいて、Modalは「重要な情報や判断をユーザーに提示する場」です。
その場が多重表示でごちゃごちゃにならないようにすることは、 ユーザー体験とシステムの信頼性を守るうえで、とても実務的で重要な技術だと言えます。
