モーダルウインドウの実装(ボタンで開く)
背景を暗くして前面に小窓を出す「モーダルウインドウ」を、HTML / CSS / JS だけで実装します。
下のデモで挙動を確認しつつ、タブ切替のコードブロック(コピー可)から必要なパートだけ持ち出せます。
コードについて
本記事のコードは AI(ChatGPT)による生成をベースに作成・調整しています。ご利用の環境でテストの上ご使用ください。
免責
本コードの利用に伴う不具合・損害について、当サイトは責任を負いません。自己責任にてご利用ください。
デモ
「モーダルを開く」→ 背景クリック / ESC / 閉じるボタン で閉じます。
お知らせ
このモーダルは、コピペで導入できる軽量実装です。
タブ切替のコードブロック(コピー可)
<!-- ===============================================
モーダル:HTML 構造
- .cp-modal …… コンポーネント全体のラッパー(スコープ)
- .btn …… 開閉トリガーボタン(開く/閉じる)
- .layer …… 全画面の半透明暗幕(背景クリックで閉じる)
- .dialog …… 本体(タイトル・本文・ボタンを内包)
- aria-* …… アクセシビリティ配慮
- tabindex="-1" …… JSでフォーカス移動を可能に
=============================================== -->
<div class="cp-modal" id="cp-modal-1">
<!-- 「開く」ボタン -->
<button class="btn" data-open-modal>モーダルを開く</button>
<!-- 背景の暗幕レイヤー -->
<div class="layer" aria-hidden="true">
<!-- ダイアログ本体 -->
<div class="dialog" role="dialog" aria-modal="true" aria-labelledby="cp-modal-title" tabindex="-1">
<h3 id="cp-modal-title">お知らせ</h3>
<p>このモーダルは、コピペで導入できる軽量実装です。</p>
<button class="btn" data-close-modal>閉じる</button>
</div> <!-- /.dialog -->
</div> <!-- /.layer -->
</div> <!-- /.cp-modal -->
/* ボタン(開く/閉じる 共通) */
.cp-modal .btn{
background:#0b6bff; /* アクセント色 */
color:#fff; /* 文字は白 */
border:none; /* 既定ボーダーは不要 */
border-radius:12px; /* 角丸でタップしやすく */
padding:10px 14px; /* 触りやすい余白 */
cursor:pointer; /* クリック状態を明確に */
}
/* オーバーレイ(初期は非表示) */
.cp-modal .layer{
position:fixed; /* 画面全体に固定 */
inset:0; /* 四辺0でフルスクリーン */
background:rgba(2,8,23,.5); /* 半透明の暗幕 */
display:grid; /* 中央寄せ */
place-items:center; /* 子要素を縦横中央に */
opacity:0; /* 初期は見えない */
pointer-events:none; /* 初期はクリック不可 */
transition:.2s; /* フェード */
}
/* 表示状態(data-open="1" が付与されたとき) */
.cp-modal[data-open="1"] .layer{
opacity:1; /* フェードイン */
pointer-events:auto; /* 背景クリックを受付 */
}
/* ダイアログ本体 */
.cp-modal .dialog{
background:#fff; /* 白地でカード風 */
color:#0f172a; /* 読みやすい文字色 */
border-radius:12px; /* 角丸 */
padding:20px; /* 内側余白 */
max-width:400px; /* 横幅上限 */
/* 必要に応じて width:90vw; を追加して極小画面に対応 */
}
/* フォーカス可視化(キーボード操作向け) */
.cp-modal .btn:focus,
.cp-modal .dialog:focus{
outline:2px solid #93c5fd;
outline-offset:2px;
}
/* 開閉ロジック(依存なしのプレーンJS) */
(() => {
const root = document.getElementById('cp-modal-1'); // ルート
if(!root) return;
const layer = root.querySelector('.layer'); // 背景
const dialog = root.querySelector('.dialog'); // 本体(フォーカス先)
const openBtn = root.querySelector('[data-open-modal]'); // 開く
const closeBtn = root.querySelector('[data-close-modal]'); // 閉じる
function onKey(e){ if(e.key==='Escape') close(); } // ESCで閉じる
function open(){
root.setAttribute('data-open','1'); // 表示トリガー
dialog && dialog.focus(); // フォーカス移動
document.addEventListener('keydown', onKey); // ESC監視
}
function close(){
root.removeAttribute('data-open'); // 非表示
document.removeEventListener('keydown', onKey); // 監視解除
openBtn && openBtn.focus(); // 元ボタンへ返す
}
openBtn && openBtn.addEventListener('click', open); // 開く
closeBtn && closeBtn.addEventListener('click', close); // 閉じる
layer && layer.addEventListener('click', (e)=>{ // 背景クリックで閉じる
if(e.target===layer) close();
});
})();
ポイント
- 開き方:ボタンで開く。表示時は親に
data-open="1"
を付与。
- 閉じ方:閉じるボタン/背景クリック/
ESC
キー。
- アクセシビリティ:開いたら
.dialog
にフォーカス、閉じたらトリガーへ返却。
- 衝突回避:
.cp-modal
スコープでテーマCSSと競合しにくく。
コメント