Tips

モーダル(ポップアップ)が作れるdialogタグとは?【showModal,close,clientX,clientY,backdrop,getBoundingClientRect】

  • このエントリーをはてなブックマークに追加

「モーダルやポップアップをサクッと作ることができない」
「JavaScriptは簡単なメソッドかライブラリしか使ったことがない」
「モーダルやポップアップを要素外をクリックしたときにも閉じれるようにしたい」

本日はそんな方に向けた、モーダルの簡単かつ最新の作り方を紹介します。

元々あった方法ですがIEの存在により実務では使いづらかったのですが、IE対策が不要になりつつある昨今にこそ知って得する方法です。

動画もあるので初学者の方でもチャレンジすることができるはずですので一読ください。

dialogタグでモーダル(ポップアップ)を最速で作る方法

まずモーダルの見た目をHTMLで作るのですがHTMLのdialogタグを使うと以下のように書くことができます。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./style.css" />
    <script
      src="https://yubinbango.github.io/yubinbango/yubinbango.js"
      charset="UTF-8"
    ></script>
    <script src="./script.js" defer></script>
  </head>
  <body>
    <button id="btn">クリック</button>
    <dialog id="dialog">
      <div>
        <p>モーダルです</p>
        <button id="modalBtn">閉じる</button>
        </form>
      </div>
    </dialog>
  </body>
</html>

画面を確認してわかることとしては、dialogタグで囲んだ部分は表示されていないということです。

dialogタグはデフォルトでは非表示になるので、ただHTMLファイルに書いただけでは画面に表示されません。

従来の方法だとモーダルの部分に事前にCSS で非表示にする設定が必要でした。

いきなり便利なポイントですね。

モーダルの表示、非表示の切り替えは簡単なJavaScriptで実現できます。

const btn = document.querySelector("#btn");
const modalBtn = document.querySelector("#modalBtn");
const dialog = document.querySelector("#dialog");

// モーダルを表示する
btn.addEventListener("click", () => {
  dialog.showModal();
});

// モーダルを非表示にする
modalBtn.addEventListener("click", () => {
  dialog.close();
});

初学者でも使用したことのある基本的な文法しか使っていません。

showModalとcloseについては初見の方がいるかもしれませんが、もともとJavaScriptで用意されているメソッドです。

おそらくモーダル、ポップアップくらいでしか使わないと思うので無理して覚える必要はないでしょう。

モーダルの表示と非表示の切り替えができているかと思います。

モーダルを作ったことのある方ならわかると思いますが、モーダル自体もいきなり画面の中央に配置されていて背景色もついていますね。

従来の方法だとモーダル自体の大きさや配置をCSS で作り込んでおく必要がありました。

モーダルのように画面の上に重ねて表示するCSS は初学者の方には結構難しいものです、それが簡単に出来てしまうのは嬉しいですね。

ちなみにログイン画面のような使い方をする場合に必要なフォームもdialogタグの中に書くことで表示できます。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./style.css" />
    <script
      src="https://yubinbango.github.io/yubinbango/yubinbango.js"
      charset="UTF-8"
    ></script>
    <script src="./script.js" defer></script>
  </head>
  <body>
    <button id="btn">クリック</button>
    <dialog id="dialog">
      <div>
        <p>モーダルです</p>
        <button id="modalBtn">閉じる</button>
        <form action="">
          <input type="text" />
          <button type="submit">送信</button>
        </form>
      </div>
    </dialog>
  </body>
</html>

またdialogタグには専用のHTML属性があり、以下のような取り消しボタンを表示するときに重宝します。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="./style.css" />
    <script
      src="https://yubinbango.github.io/yubinbango/yubinbango.js"
      charset="UTF-8"
    ></script>
    <script src="./script.js" defer></script>
  </head>
  <body>
    <button id="btn">クリック</button>
    <dialog id="dialog">
      <div>
        <p>モーダルです</p>
        <button id="modalBtn">閉じる</button>
        <form action="">
          <input type="text" />
          <button type="submit">送信</button>
         <!-- ここを追加 -->
          <button type="submit" formmethod="dialog">取り消し</button>
        </form>
      </div>
    </dialog>
  </body>
</html>

一度入力して送信せずに取り消しボタンをクリックするとモーダルが閉じます。

その後に再度モーダルを表示すると入力していた状態から再開することができるようになります。

「formmethod=”dialog”」という属性をボタンの部分に設定するだけです。

今回はデータの送信までは解説しませんが、dialogタグの中にはいろんなものを書くことを知ってもらいたかったので紹介しました。

dialogタグで作ったモーダルにCSS を当てる方法

現状のモーダルでも大変便利ですが、追加でCSS を当てる方法も紹介しておきます。

以下のようにCSS をただ書いていくだけなので初学者の方でも対応できるでしょう。

#dialog {
  border: none;
  border-radius: 8px;
  width: 200px;
  height: 200px;
}

特徴的なものがあるとすれば背景についてです。

こちらはbackdropという擬似要素を使用すればdialogタグに背景を指定できるようになります。

#dialog {
  border: none;
  border-radius: 8px;
  width: 200px;
  height: 200px;
}
/* ここを追加 */
#dialog::backdrop {
  background: rgba(0, 0, 0, 0.3);
}

backdropはもともと全画面表示する時の背景を指定するためのものです。

動画の全画面表示やモーダルで使用するための擬似要素なので、そこまで登場頻度は多くないかもしれませんね。

モーダルの範囲外をクリックして閉じるようにする方法

dialogタグだからということではないですが、モーダルの作成でよく登場する要件で「モーダルの範囲外をクリックしても閉じるようにする」というものをやってみます。

近年はUI/UXがとても重視されているので、ボタン以外でも閉じれるようにすることが求められます。

const btn = document.querySelector("#btn");
const modalBtn = document.querySelector("#modalBtn");
const dialog = document.querySelector("#dialog");

btn.addEventListener("click", () => {
  dialog.showModal();
});

modalBtn.addEventListener("click", () => {
  dialog.close();
});

// ここを追加
dialog.addEventListener("click", (e) => {
  const dialogPosition = dialog.getBoundingClientRect();
  if (
    e.clientX < dialogPosition.left ||
    e.clientX > dialogPosition.right ||
    e.clientY < dialogPosition.top ||
    e.clientY > dialogPosition.bottom
  ) {
    dialog.close();
  }
});

ポイントは「モーダルの位置」「クリックした位置」をどのように取得するかです。

それさえわかれば「モーダル位置の外をクリックしたら閉じる」ということにつながるからです。

まずモーダルの位置についてはgetBoundingClientRectというメソッドで取得できます。

getBoundingClientRectは要素の寸法と位置を数値で取得します。

位置については画面を四角形として「上辺」「左辺」「右辺」「下辺」の4つの辺からの距離を自動で計算してくれます。

コンソールで見てみると以下のようになっています。

const btn = document.querySelector("#btn");
const modalBtn = document.querySelector("#modalBtn");
const dialog = document.querySelector("#dialog");

btn.addEventListener("click", () => {
  dialog.showModal();
});

modalBtn.addEventListener("click", () => {
  dialog.close();
});


dialog.addEventListener("click", (e) => {
  const dialogPosition = dialog.getBoundingClientRect();
  // ここを追加(モーダルを表示して、モーダルの中をクリックしたときにコンソールに表示する)
  console.log(dialogPosition);

  if (
    e.clientX < dialogPosition.left ||
    e.clientX > dialogPosition.right ||
    e.clientY < dialogPosition.top ||
    e.clientY > dialogPosition.bottom
  ) {
    dialog.close();
  }
});

オブジェクトの形式で数値が取得されているのがわかります。

「bottom:画面の下辺からの距離」
「left:画面の左辺からの距離」
「right:画面の右辺からの距離」
「top:画面の上辺からの距離」

といった具合に画面の端からの距離でモーダルの位置を特定するわけです。

続いてクリックした位置についてです。

こちらはaddEventListenerの引数でeを取っていたのですが、イベントでマウスクリックを検知出来てclientXとclientYというものが使用できます。

clientXはマウスクリックされた水平地点を数値で取得します。

画面の左端を0として右にずれるほど数値が増えていくようなイメージです。

clientYはマウスクリックされた垂直地点を数値で取得します。

画面の上端を0として下にずれるほど数値が増えていくようなイメージです。

こちらもコンソールで確認しておきましょう。

const btn = document.querySelector("#btn");
const modalBtn = document.querySelector("#modalBtn");
const dialog = document.querySelector("#dialog");

btn.addEventListener("click", () => {
  dialog.showModal();
});

modalBtn.addEventListener("click", () => {
  dialog.close();
});


dialog.addEventListener("click", (e) => {
  const dialogPosition = dialog.getBoundingClientRect();
  console.log(dialogPosition);

  // ここを追加(モーダルを表示して、画面のどこかをクリックしたときにコンソールに表示)
  console.log("X: ", e.clientX);
  console.log("Y: ", e.clientY);

  if (
    e.clientX < dialogPosition.left ||
    e.clientX > dialogPosition.right ||
    e.clientY < dialogPosition.top ||
    e.clientY > dialogPosition.bottom
  ) {
    dialog.close();
  }
});

モーダルの時と同じように数値で取得されていますね。

モーダルの時と違うのはマウスクリックはクリックする場所によって数値が変わります。

適当にいろんな場所をクリックすると数値が変わっているのが分かります。

あとは数値の大小を比較するだけです、モーダルの内側でクリックされたのか外側でクリックされたのかを条件分岐で判定する形です。

const btn = document.querySelector("#btn");
const modalBtn = document.querySelector("#modalBtn");
const dialog = document.querySelector("#dialog");

btn.addEventListener("click", () => {
  dialog.showModal();
});

modalBtn.addEventListener("click", () => {
  dialog.close();
});

// モーダルの外をクリックしても閉じるようにする
dialog.addEventListener("click", (e) => {
  // 要素の寸法と画面上の相対位置を取得
  const dialogPosition = dialog.getBoundingClientRect();
  console.log(dialogPosition);
  // イベント(クリック)が発生した時点の画面上の相対位置
  console.log("X: ", e.clientX);
  console.log("Y: ", e.clientY);
  if (
    e.clientX < dialogPosition.left ||
    e.clientX > dialogPosition.right ||
    e.clientY < dialogPosition.top ||
    e.clientY > dialogPosition.bottom
  ) {
    dialog.close();
  }
});

これでモーダルの外側をクリックしたときに閉じるようになりましたね。

dialogタグに関わらず他のケースでも使用できる考え方なので初学者の方も練習してみてください。

また今回参考にした本はこちらになります。

良ければこちらからどうぞ。

  • このエントリーをはてなブックマークに追加