プログラミング

[JavaScript] できるだけシンプルに、画面がガタつかないモーダルを実装する方法

たかがモーダル、されどモーダル

ボタンをクリックすると中央にモーダルが表示されて、背景がグレーアウトするやつ。

何回か実装する機会があって、その度にイライラ満点なので忘れないうちにまとめて置こうと思います。

というのも、モーダルを表示した際に背景を固定すると画面がガタついたり、うまく中央に配置されなかったり、トップに貼り付いちゃったり、まぁほんとにいろんな問題が出てきちゃうんですよね。

コンテンツが少ない時は、position: fixed; top:0;とかでいいんですよ、

けどコンテンツが多くて、ページの途中でモーダルを表示させたいとかいう場合にこれがうまくいかない…

これらの問題を全て解決するためにはどうするのかというのを調べると、

JavaScript ですんごいめんどくさい処理してたりする記事が多くて、

全然わからんしもっとシンプルに、ミニマムにしたい!!!!

ということで前置きが長くなりましたが、早速見ていきましょう。

前回ブログはこちらから↓

ソースコード

まず、動きについてはこちらのサンプルをご参考ください。

サンプルはコチラ

html はこれだけ用意しておきます。

<!-- モーダルを表示するボタン -->
<div class="button">モーダルを表示</div>

<!-- モーダル -->
<div class="modal__wrapper">
    <div class="overlay"></div>
    <div class="modal">モーダルが表示できたよ!</div>
</div>

CSSはこちら。

body {
    margin: 0;  /* モーダルを全画面表示させるため余白は 0 にする*/
    padding: 0;
    overflow-y: scroll; /* 縦スクロールバーを常に表示 */
  }
/* モーダル外枠 */
  .modal__wrapper { 
    position: fixed;
    display: none;
    width: 100%;
    height: 100vh;
    top: 0;
  }
/* モーダル本体 */
  .modal {
    position: absolute;
    top: 50%;
    left: 50%;
    background-color: #fff;
    padding: 100px;
    transform: translate(-50%, -50%);
  }
/* モーダルの背景 */
  .overlay {
    height: 100vh;
    background-color: rgba(0, 0, 0, 0.7);
    cursor: pointer;
  }

/* 以下は JavaScript でクラス付け替え時に使用する */
  .visible {
    display: block;
  }
  .invisible {
    display: none;
  }
  .backgroundfix {
    position: fixed;
    width: 100%;
  }

JavaScript の処理はこちら。

const body = document.body;  // body 要素
const modal = document.querySelector('.modal__wrapper');  // モーダル要素
const overlay = document.querySelector('.overlay');  // モーダルの背景の要素
const button = document.querySelector('.button');   // モーダルを表示させるボタン

// ボタンをクリックした時にモーダルを表示する
button.addEventListener('click', () => {
  body.style.top = `-${window.scrollY}px`;  // スクロール量を取得して body の高さとする
  body.classList.add('backgroundfix');  // body 要素を固定してスクロールできないようにする
  modal.classList.add('visible'); // モーダルを表示
}, false);

// モーダルの背景をクリックした時にモーダルを閉じる
overlay.addEventListener('click', () => {
  modal.classList.remove('visible');  // モーダルを非表示に
  body.classList.remove('backgroundfix');  // body の固定を解除
  const top = body.style.top;  // body の高さを取得
  const topHeight = top.replace('px', '').replace('-', '');  // top から - と px を除去
  window.scrollTo(0, topHeight); // topHeight の高さまで移動
}, false);

これでコンテンツの途中でモーダルを設置しても常にモーダルが画面中央に配置され、

モーダルを表示している時は背景が固定されてスクロールができなくなり、

モーダルの外側のグレー部分をクリックするとモーダルが消える挙動が実現できました。

もちろん、モーダル表示時の背景ガタつきもなし!

1つ難点を言うとすれば、モーダルが表示された時も縦のスクロールバーが出てしまうことかなぁ。。

背景のガタつきって、縦スクロールバーの幅の分だけずれてしまうんですよね。

なので body に overflow-y: scroll; を指定しています。

モーダルを表示した時はスクロールバーを絶対に表示させたくない!って場合はこの方法だとダメですけど、そんな細かいこと(?)気にしないよって場合は使えるかな?

まとめ

できるだけ簡単に、と思いましたが、伝わったでしょうかね。

周辺の構造によっては上記のソースでうまくいかない場合もあるかもしれませんが、

何かの参考になれば幸いです。

ここまでお読みいただき、ありがとうございました!