1日目のゴールと今日やること
1日目のテーマは
「モーダルウィンドウを“クラス”として設計し、きれいに開閉を制御できるようになる」
ことです。
今日のゴールは、ざっくり言うとこの3つです。
- モーダルを「1つのクラス」として設計してみる
- UI の状態(開いている / 閉じている)をコードで管理する感覚をつかむ
- 背景クリックと ESC キーで閉じる処理を、イベント伝播を意識しながら書く
「なんとなく動けばOK」ではなく、
“あとから読んでも分かりやすいモーダル” を目指します。
モーダルウィンドウとは何かを整理する
モーダルの役割
モーダルは、画面の上に「手前のレイヤー」として出てくるウィンドウです。
- 背景は暗くなる
- ユーザーに「今はこのモーダルに集中してね」と伝える
- OK / キャンセルなどの操作をさせる
よくある例は、
「本当に削除しますか?」の確認ダイアログです。
今日作るモーダルの仕様
1日目では、シンプルにこうします。
- ボタンを押すとモーダルが開く
- モーダル内の「閉じる」ボタンで閉じる
- 背景(黒い部分)をクリックすると閉じる
- ESC キーでも閉じる
これを クラスで管理する のが今日のメインテーマです。
HTML と CSS の前提イメージ
HTML の構造イメージ
<button id="openModal">モーダルを開く</button>
<div class="modal" id="myModal">
<div class="modal__backdrop"></div>
<div class="modal__content">
<p>モーダルの中身です</p>
<button class="modal__close">閉じる</button>
</div>
</div>
ポイントは「モーダル全体」と「背景」と「中身」が分かれていることです。
- .modal … 全体(表示 / 非表示を切り替える)
- .modal__backdrop … 黒い背景
- .modal__content … 白いウィンドウ本体
CSS はここでは細かくやりませんが、.modal に display: none; を付けておいて、
「開くときにクラスを付けて表示する」という形を想定します。
クラスでモーダルを管理するという発想
なぜクラスにするのか
モーダルを「バラバラの関数」で管理すると、こうなりがちです。
function openModal() { ... }
function closeModal() { ... }
function onBackdropClick() { ... }
function onKeydown() { ... }
JavaScriptこれでも動きますが、
- どの関数がどのモーダルに関係しているのか分かりにくい
- モーダルが2つになった瞬間に地獄になる
そこで、「モーダル1つ = クラス1つ」 という形にします。
クラスのイメージ
class Modal {
constructor(rootElement) {
this.root = rootElement;
}
open() {}
close() {}
bindEvents() {}
}
JavaScript「モーダルに関することは、このクラスの中に閉じ込める」
という考え方です。
モーダルクラスの基本設計
コンストラクタで「要素」を握る
class Modal {
constructor(root) {
this.root = root;
this.backdrop = root.querySelector(".modal__backdrop");
this.content = root.querySelector(".modal__content");
this.closeButton = root.querySelector(".modal__close");
this.isOpen = false;
this.handleBackdropClick = this.handleBackdropClick.bind(this);
this.handleKeydown = this.handleKeydown.bind(this);
this.bindEvents();
}
}
JavaScriptここでやっていることを分解すると:
- root … モーダル全体の要素(
#myModal) - backdrop … 背景の要素
- content … 中身の要素
- closeButton … 閉じるボタン
- isOpen … 「今開いているかどうか」を持つフラグ
- bind しているのは、イベントで this を正しく保つため
深掘りポイント:UI 状態を「isOpen」で持つ意味
isOpen は、
「今このモーダルは開いているのか?」
を表すフラグです。
これを持っておくと、
- すでに開いているのにまた open しようとしたときに無視できる
- ESC キーで閉じるとき、「開いているときだけ反応する」ようにできる
など、状態に応じた制御 がしやすくなります。
開く / 閉じるの実装
open メソッド
open() {
if (this.isOpen) return;
this.root.classList.add("is-open");
this.isOpen = true;
document.addEventListener("keydown", this.handleKeydown);
}
JavaScriptclose メソッド
close() {
if (!this.isOpen) return;
this.root.classList.remove("is-open");
this.isOpen = false;
document.removeEventListener("keydown", this.handleKeydown);
}
JavaScript深掘りポイント:UI と状態とイベントをセットで考える
open でやっていることは 3 つです。
- 見た目を変える(クラスを付ける)
- 状態を変える(isOpen = true)
- キーボードイベントを登録する
close ではその逆をやっています。
「見た目」「状態」「イベント」
この3つは、UI を設計するときにいつもセットで考えます。
背景クリックで閉じる処理とイベント伝播
背景クリックのイベント登録
bindEvents() {
if (this.backdrop) {
this.backdrop.addEventListener("click", this.handleBackdropClick);
}
if (this.closeButton) {
this.closeButton.addEventListener("click", () => this.close());
}
}
JavaScript背景クリックのハンドラ
handleBackdropClick(event) {
this.close();
}
JavaScriptこれだけでも「背景をクリックしたら閉じる」は実現できます。
ここで出てくる問題:中身をクリックしても閉じてしまう?
今回の構造では、.modal__backdrop と .modal__content は別要素なので、
背景だけにイベントを付けていれば問題は起きません。
ただし、よくある別パターンとして
「モーダル全体にクリックイベントを付けて、
中身のクリックは無視したい」というケースがあります。
そのときに重要になるのが イベント伝播 です。
イベント伝播を意識したクリック制御の例
モーダル全体にクリックイベントを付ける場合
<div class="modal" id="myModal">
<div class="modal__content">
...
</div>
</div>
JavaScriptこの構造で「背景クリックで閉じる」をやろうとして、.modal にクリックイベントを付けるとします。
this.root.addEventListener("click", () => {
this.close();
});
JavaScriptすると、.modal__content をクリックしてもイベントが親に伝播して、
モーダルが閉じてしまいます。
そこで stopPropagation
this.content.addEventListener("click", (event) => {
event.stopPropagation();
});
JavaScriptこうすると、.modal__content 内で発生したクリックイベントは.modal まで届かなくなります。
深掘りポイント:イベント伝播は「内側 → 外側」に流れる
クリックイベントは、
一番内側の要素
→ 親
→ さらに親
→ …
→ document
という順番で伝わっていきます。
event.stopPropagation() は
「これ以上外側に伝えないで」
という指示です。
モーダルのように
「中身のクリックは無視したいけど、背景のクリックは拾いたい」
という UI では、イベント伝播の理解がとても重要になります。
ESC キーで閉じる処理
キーボードイベントのハンドラ
handleKeydown(event) {
if (event.key === "Escape") {
this.close();
}
}
JavaScriptopen / close との連携
すでに書いたように、
open で document.addEventListener("keydown", this.handleKeydown);
close で removeEventListener をしています。
深掘りポイント:必要なときだけイベントを登録する
常に keydown を監視していると、
- モーダルが閉じているのに ESC で何かが動いてしまう
- ページ全体のキーボード操作に悪影響が出る
といった問題が起きます。
「モーダルが開いているときだけ ESC を受け付ける」
という設計にすることで、
UI の一貫性と安全性が高まります。
実際にインスタンスを作って動かす
初期化コード
const modalElement = document.getElementById("myModal");
const openButton = document.getElementById("openModal");
const modal = new Modal(modalElement);
openButton.addEventListener("click", () => {
modal.open();
});
JavaScriptこれで、
- ボタンを押すとモーダルが開く
- 閉じるボタンで閉じる
- 背景クリックで閉じる
- ESC キーで閉じる
という一連の流れが、
Modal クラスの中にきれいにまとまった状態 になります。
1日目のまとめ
今日あなたがやったことを整理すると、こうなります。
- モーダルを「クラス」として設計する発想を持った
- UI 状態(isOpen)をフラグで管理する感覚をつかんだ
- 開く / 閉じるで「見た目」「状態」「イベント」をセットで制御した
- 背景クリックとイベント伝播の関係を理解した
- ESC キー対応を「開いているときだけ有効」にする設計を体験した
どれも、実務でモーダルを実装するときに
必ず意識するポイントです。
今日いちばん深く理解してほしいこと
1日目の本質は、
「UI コンポーネント(モーダル)を“クラス + 状態 + イベント”で設計する」
という感覚です。
- モーダルに関することは Modal クラスに閉じ込める
- isOpen で状態を持つ
- open / close で UI とイベントを切り替える
- イベント伝播を理解して、クリックの挙動をコントロールする
ここまでイメージできていたら、
あなたはもう「とりあえず動けばいい」から
“設計して作る側” に一歩踏み出しています。
2日目では、この Modal クラスを
「複数モーダル対応」「コールバック対応」などに発展させていきます。

