本文へスキップ
  1. Web BENTO JS
  2. カテゴリ一覧
  3. モーダル・ドロワー
01 / MODAL 12 パーツ

JSモーダル・ドロワー実装 12選

コピペで動くモーダル・ドロワー(ダイアログ / パネル)のサンプル集です。プレビューは実際にボタンで開閉できます。HTML / CSS / JS タブでコードを確認し、全部コピーを押せば1回の貼り付けで完成します。すべて依存ライブラリなしのバニラJSで、data-bnto-* 属性による自動初期化式です。

該当するパーツが見つかりませんでした。

ベーシックモーダル

<div class="bnto-mdl" data-bnto-mdl>
  <button type="button" class="op">モーダルを開く</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="お知らせ">
    <div class="ov" data-close></div>
    <div class="pn">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <h3 class="tt">モーダルタイトル</h3>
      <p class="tx">ここに本文を書きます。</p>
    </div>
  </div>
</div>
使い方のコツ

デモはカード内に表示していますが、実際のページ全体に被せるときは .bxposition:fixed に変えるだけです。閉じるボタン・背景・Escの3経路で閉じられるのがモーダルUXの基本形です。

確認ダイアログ

<div class="bnto-cfm" data-bnto-cfm>
  <button type="button" class="op">削除する</button>
  <!-- 結果表示用(不要なら削除OK。data-result 属性にも入ります) -->
  <span class="rs" aria-live="polite"></span>
  <div class="bx" role="dialog" aria-modal="true" aria-label="確認">
    <div class="ov"></div>
    <div class="pn">
      <p class="tx">本当に削除しますか?<br>この操作は取り消せません。</p>
      <div class="btns">
        <button type="button" class="no">キャンセル</button>
        <button type="button" class="ok">OK</button>
      </div>
    </div>
  </div>
</div>
使い方のコツ

結果は data-result="ok / cancel" にも入るので、close() 内に実処理を書けば window.confirm の置き換えになります。破壊的操作では初期フォーカスをキャンセル側に置くのが安全です。

ボトムシート

<div class="bnto-sheet" data-bnto-sheet>
  <button type="button" class="op">シートを開く</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="メニュー">
    <div class="ov" data-close></div>
    <div class="pn">
      <span class="bar" aria-hidden="true"></span>
      <!-- 項目は button でも a でもOK。data-close で選択後に閉じます -->
      <button type="button" class="mi" data-close>プロフィールを見る</button>
      <button type="button" class="mi" data-close>リンクをシェアする</button>
      <button type="button" class="mi dg" data-close>削除する</button>
    </div>
  </div>
</div>
使い方のコツ

スマホの「…」メニューや共有メニューの定番UIです。各項目のクリック処理は .mi にリスナーを足すだけで済みます。項目数が多い場合は .pnmax-heightoverflow:auto を追加しましょう。

右ドロワー

<div class="bnto-drw" data-bnto-drw>
  <button type="button" class="op"><span aria-hidden="true">≡</span> メニュー</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="ナビゲーション">
    <div class="ov" data-close></div>
    <div class="pn">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <span class="hd">MENU</span>
      <!-- 実際のナビでは <a href="..."> に置き換えてOK -->
      <button type="button" class="mi" data-close>ホーム</button>
      <button type="button" class="mi" data-close>サービス</button>
      <button type="button" class="mi" data-close>お問い合わせ</button>
    </div>
  </div>
</div>
使い方のコツ

スマホ用グローバルナビの定番です。実運用ではメニュー項目を <a href> に置き換えます(data-close を付ければ遷移前に閉じる動きも入ります)。幅は .pnwidth で調整できます。

左ドロワー

<div class="bnto-drwl" data-bnto-drwl>
  <button type="button" class="op"><span aria-hidden="true">≡</span> サイドメニュー</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="サイドナビゲーション">
    <div class="ov" data-close></div>
    <div class="pn">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <span class="hd">NAVIGATION</span>
      <!-- 実際のナビでは <a href="..."> に置き換えてOK -->
      <button type="button" class="mi" data-close>ダッシュボード</button>
      <button type="button" class="mi" data-close>プロジェクト</button>
      <button type="button" class="mi" data-close>設定</button>
    </div>
  </div>
</div>
使い方のコツ

右ドロワーとの違いはCSSの right:0left:0 と、スライド方向・影の向きだけです。ロゴが左上にあるサイトでは、左から出すほうが視線の流れと合いやすいです。

フルスクリーンメニュー

<div class="bnto-fsm" data-bnto-fsm>
  <button type="button" class="op"><span aria-hidden="true">≡</span> 全画面メニュー</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="メニュー">
    <div class="pn">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <!-- 実際のナビでは <a href="..."> に置き換えてOK -->
      <button type="button" class="mi" data-close>ABOUT</button>
      <button type="button" class="mi" data-close>WORKS</button>
      <button type="button" class="mi" data-close>CONTACT</button>
    </div>
  </div>
</div>
使い方のコツ

ポートフォリオやブランドサイトで人気の演出です。背景を background:linear-gradient(...) にしたり、各 .mianimation-delay を足して順番にフェードインさせるとさらにリッチになります。

画像ライトボックス

<div class="bnto-lbx" data-bnto-lbx>
  <div class="ths">
    <!-- デモはCSSグラデで画像を代用。実画像なら
         style="background-image:url(photo.jpg)" にするだけ -->
    <button type="button" class="th" data-cap="PHOTO 01" aria-label="写真1を拡大"
      style="background:linear-gradient(135deg,#FFB36B,#FF5A4D);"></button>
    <button type="button" class="th" data-cap="PHOTO 02" aria-label="写真2を拡大"
      style="background:linear-gradient(135deg,#7BD5F5,#3D6DE0);"></button>
  </div>
  <div class="bx" role="dialog" aria-modal="true" aria-label="画像の拡大表示">
    <div class="ov" data-close></div>
    <div class="fig">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <div class="bg" role="img" aria-label="拡大画像"></div>
      <span class="cp"></span>
    </div>
  </div>
</div>
使い方のコツ

実画像で使うときはサムネに data-full="big.jpg" を持たせ、JSで big.style.backgroundImage = 'url(' + th.dataset.full + ')' とすると高解像度版に切り替えられます。data-cap がキャプションになります。

<dialog> 要素

HTML標準のモーダルです。showModal() だけで最前面表示・Escで閉じる・フォーカス管理までブラウザが面倒を見てくれます。

dialog要素(標準API)

<div class="bnto-dlg" data-bnto-dlg>
  <button type="button" class="op">dialogを開く</button>
  <!-- role="dialog" も aria-modal も不要。要素が標準で持っています -->
  <dialog class="dl" aria-label="お知らせ">
    <h3 class="tt">ダイアログタイトル</h3>
    <p class="tx">ここに本文を書きます。</p>
    <button type="button" class="x" data-close>閉じる</button>
  </dialog>
</div>
使い方のコツ

Esc対応・フォーカストラップ・最前面表示(top layer)が全部ブラウザ標準です。このパーツだけは仕組み上デモでもページ全体の中央に表示されます。背景クリックで閉じたい場合は dl.addEventListener('click', e => { if (e.target === dl) dl.close(); }) を1行追加してください。

2秒後にポップアップが1回だけ自動表示されます

時間差ポップアップ

<!-- data-delay はミリ秒。data-scroll="300" にするとスクロール量(px)で表示 -->
<div class="bnto-pop" data-bnto-pop data-delay="2000">
  <p class="ph">2秒後にポップアップが1回だけ自動表示されます</p>
  <!-- デモ用の再体験ボタン(本番では削除OK) -->
  <button type="button" class="rst">リセットして再体験</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="キャンペーンのお知らせ">
    <div class="ov" data-close></div>
    <div class="pn">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <span class="bd">CAMPAIGN</span>
      <h3 class="tt">期間限定クーポン配布中</h3>
      <p class="tx">ここに告知文を書きます。</p>
      <button type="button" class="cta" data-close>くわしく見る</button>
    </div>
  </div>
</div>
使い方のコツ

キャンペーン告知やメルマガ登録の定番UIです。一度閉じたら sessionStorage に記憶するので、同じタブで見ている間はしつこく再表示されません(タブを閉じるとリセット)。翌日まで出したくない場合は localStorage +有効期限の保存に置き換えてください。表示条件は data-delay="2000"(ミリ秒)か data-scroll="300"(スクロール量px)で調整できます。

マウスをページ上端の外へ動かすと1回だけ表示されます

離脱防止ポップアップ

<div class="bnto-exm" data-bnto-exitmodal>
  <p class="ph">マウスをページ上端の外へ動かすと1回だけ表示されます</p>
  <!-- デモ用の再体験ボタン(本番では削除OK) -->
  <button type="button" class="rst">リセットして再体験</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="離脱前のお知らせ">
    <div class="ov" data-close></div>
    <div class="pn">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <span class="bd">WAIT</span>
      <h3 class="tt">ちょっと待ってください</h3>
      <p class="tx">ここに引き止めの文を書きます。</p>
      <button type="button" class="cta" data-close>特典を見る</button>
    </div>
  </div>
</div>
使い方のコツ

タブを閉じる・URLバーへ向かうなど、マウスが上端から出た瞬間(mouseout かつ clientY <= 0)に表示するexit-intentの定番実装です。タッチ操作のスマホでは発火しないため、モバイルも対象にするなら「時間差ポップアップ」との併用がおすすめです。翌日まで出したくない場合は sessionStoragelocalStorage +有効期限に置き換えてください。

ステップウィザード

<!-- .st と .dots の i は同じ数だけ並べる -->
<div class="bnto-wiz" data-bnto-wizardmodal>
  <button type="button" class="op">ウィザードを開く</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="セットアップ">
    <div class="ov" data-close></div>
    <div class="pn">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <div class="dots" aria-hidden="true"><i class="is-on"></i><i></i><i></i></div>
      <div class="st is-active">
        <h3 class="tt">STEP 1</h3>
        <p class="tx">1つ目の内容</p>
      </div>
      <div class="st">
        <h3 class="tt">STEP 2</h3>
        <p class="tx">2つ目の内容</p>
      </div>
      <div class="st">
        <h3 class="tt">STEP 3</h3>
        <p class="tx">3つ目の内容</p>
      </div>
      <div class="nav">
        <button type="button" class="bk">戻る</button>
        <button type="button" class="nx">次へ</button>
      </div>
    </div>
  </div>
</div>
使い方のコツ

チュートリアルや初期設定の案内に便利なUIです。ステップを増やすときは .st.dots 内の <i>同じ数だけ追加してください。ステップ数はJSが自動で数えます。完了時に登録処理などを行いたい場合は、JS内の「ここで完了時の処理を」の行に追記します。

フォーム付きモーダル

<div class="bnto-frm" data-bnto-formmodal>
  <button type="button" class="op">お問い合わせ</button>
  <div class="bx" role="dialog" aria-modal="true" aria-label="お問い合わせフォーム">
    <div class="ov" data-close></div>
    <div class="pn">
      <button type="button" class="x" data-close aria-label="閉じる">✕</button>
      <!-- 入力欄は自由に追加OK(required などの標準バリデーションが効きます) -->
      <form class="fm">
        <h3 class="tt">お問い合わせ</h3>
        <label class="lb">メールアドレス
          <input class="ip" type="email" name="email" placeholder="[email protected]" required>
        </label>
        <button type="submit" class="sb">送信する</button>
      </form>
      <!-- 送信後に表示されるお礼画面 -->
      <div class="tks" hidden>
        <h3 class="tt">送信しました</h3>
        <p class="tx">お礼のメッセージを書きます。</p>
        <button type="button" class="cta" data-close>閉じる</button>
      </div>
    </div>
  </div>
</div>
使い方のコツ

メルマガ登録や資料請求の定番UIです。実際に送信するには、JS内のコメント「実運用ではここを fetch などの送信処理に差し替え」の位置に送信コードを書きます。入力欄は <label class="lb"> ごと増やすだけでOKで、requiredtype="email" によるブラウザ標準のバリデーションもそのまま効きます。

JSモーダル・ドロワー実装の基礎知識

モーダル(ダイアログ)は、ページの上に重ねて今いちばん見てほしい内容だけに集中してもらうためのUIです。お知らせ・確認・フォーム・画像の拡大など用途は幅広く、横から出せばドロワー(ナビ向け)、下から出せばボトムシート(モバイル向け)と、出し方のバリエーションで呼び名が変わります。このページのサンプルはすべて依存ライブラリなしのバニラJavaScriptで、jQueryもフレームワークも不要です。

使い方は、HTML / CSS / JSの3タブをそれぞれ貼り付けるか、「全部コピー」ボタンでひとまとめになったコードを1回貼り付けるだけです。初期化は data-bnto-* 属性の自動検出式なので、同じページに何個置いても、うっかりコードを2回貼っても壊れません。なお、デモはカード内に収めるためコンテナを position:absolute にしています。実際のページ全体に被せるときは position:fixed に変えるだけです。

背面スクロールを固定する(body固定)

モーダルを開いたのに背面のページがスクロールできてしまうと、ユーザーは現在地を見失います。定番の対策は、開くときに document.body.style.overflow = 'hidden' を設定し、閉じるときに空文字で戻す方法です。ベーシックモーダルのJSに実装済みなので、他のパーツにもそのまま移植できます。

Escキーとフォーカス管理

モーダルUXの基本は「開けたら必ず、複数の方法で閉じられる」ことです。×ボタン・背景クリック・Escキーの3経路を用意し、閉じたあとはフォーカスを開いたボタンへ返すのがアクセシビリティの定石です。さらに role="dialog"aria-modal="true" を付ければ、スクリーンリーダーにも「今はダイアログの中」だと伝わります。

HTML標準のdialog要素という選択肢

モダンブラウザなら <dialog> 要素と showModal() で、最前面表示(top layer)・Escで閉じる・フォーカストラップ・背景レイヤー(::backdrop)までブラウザが標準処理してくれます。JSは数行で済むため、新規実装ならまず検討したい選択肢です。細かい演出や親要素内に収める表示が必要な場合は、自前実装のパーツを使い分けましょう。

「全部コピー」と「分けて貼る」の使い分け

「全部コピー」を使うと、HTML・CSS・JavaScriptがひとつの自己完結ブロックとしてコピーされるので、1回の貼り付けだけですぐ動きを確認できます。動作のお試しや、1ページ完結のLPに組み込むときはこの方法が手軽です。

一方、本番サイトへ本格的に組み込む場合は、CSSはスタイルシート(.cssファイル)へ、JavaScriptは</body>直前や共通の.jsファイルへ、HTMLは使いたい場所へと分けて貼るのが一般的です。スタイルとスクリプトを1か所に集約できるため、複数ページ・複数パーツで使い回すときの管理がしやすく、ブラウザのキャッシュも効きやすくなります。なお、同じパーツをページ内で何度も使う場合でも、CSSとJSを貼るのは1回だけで大丈夫です(初期化スクリプトが該当要素すべてに自動で効きます)。

よくある質問

コピーしたモーダルはそのまま動きますか?
はい。「全部コピー」を押すとHTML・CSS・JSがひとつの自己完結ブロックになってコピーされ、ページに貼り付けるだけで動きます。ライブラリの読み込みは一切不要です。
デモはカードの中に表示されますが、ページ全体に表示するには?
サンプルはデモ用にモーダルを position:absolute で親要素の中に表示しています。ページ全体に被せたい場合は、モーダルのコンテナを position:fixed に変更するだけです。CSSタブ内のコメントでも案内しています。
モーダル表示中に背面がスクロールしてしまいます。
ベーシックモーダルのJSのように、開くときに bodyoverflow:hidden を設定し、閉じるときに元へ戻すのが定番の対策です。数行のコードなので、ほかのパーツにも簡単に移植できます。
アクセシビリティには対応していますか?
各モーダルに role="dialog"aria-modal="true" を付与し、Escキーで閉じる・閉じたらフォーカスを開くボタンへ戻す挙動を実装しています。HTML標準の dialog 要素版なら、これらの多くをブラウザが標準で処理してくれます。
このページのパーツはAIコーディングツールでも使えますか?
はい。Claude CodeやCursorのようにWebページを読めるAIには、このページのURLとパーツ名(例:「確認ダイアログ」)を伝えるだけで実装を指示できます。AIがWebを読めない場合は、「全部コピー」で取得した自己完結ブロックをチャットに貼り付けて調整を頼むのが確実です。改変後のコードは、公開前に動作確認することをおすすめします。

関連カテゴリ

モーダル・ドロワーと組み合わせて使いやすいパーツはこちらです。