Help us understand the problem. What is going on with this article?

スマホで使えるシンプルなモーダルウィンドウ

More than 1 year has passed since last update.

モーダルウィンドウを実装したい、でもライブラリ使うほどの要件ではない。
といった時に使える自前でパパっとモーダルウィンドウの実装。

やりたいこと

  • 要素(ボタンやaタグ)をクリックしたらモーダルウィンドウが表示される
  • モーダルウィンドウは画面の中央に表示
  • モーダル内の要素が画面の高さを超えた場合、スクロールできる
  • 「閉じるボタン」を押したらモーダルウィンドウが閉じる
  • オーバーレイをクリックしたらモーダルウィンドウが閉じる
html
<!-- モーダルを表示させるためのボタンにはclass="modalBtn"を付与 -->
<button class="modalBtn" type="button">モーダル表示ボタン</button>
<p>aタグでも<a class="modalBtn" href="#">モーダル表示</a></p>

<!-- モーダルウィンドウテンプレート -->
<div class="modal">
  <div class="modal_container">
    <div class="modal_inner">
      <div class="modal_content">
        <div>ここにモーダルで表示させたいものをいれる。</div>
        <!-- モーダルを閉じるボタンにはclass="modalClose"を付与 -->
        <button class="modalClose" type="button">閉じる</button>
        <p>aタグでも<a class="modalClose" href="#">閉じる</a></p>
      </div>
    </div>
  </div>
</div>
scss
// モーダルウィンドウの上下の余白(コンテンツ内の要素が画面の高さを超えた場合に適用される)
$modal_padding_top-bottom: 20px;

// モーダルウィンドウの左右の余白
$modal_padding_left-right: 20px;

.modal {
  visibility: hidden;
  overflow-y: scroll;
  opacity: 0;
  position: fixed;
  z-index: 100;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 0 $modal_padding_left-right;
  transition: opacity 0.3s, visibility 0s 0.3s;
  background: rgba(0, 0, 0, 0.8);

  &.is-active {
    visibility: visible;
    opacity: 1;
    transition: opacity 0.3s, visibility 0s 0s;
  }
}

.modal_container {
  display: flex;
  min-height: 100%;
  justify-content: center;
  align-items: center;

  // 以下、IE11 ハック(IEでこれがないと上下中央寄せが出来ない)
  _:-ms-lang(x)::-ms-backdrop,
  & {
    min-height: 100vh;
  }

  &::after {
    content: '';
    min-height: inherit;
    font-size: 0;
  }
}

.modal_inner {
  margin: $modal_padding_top-bottom auto;
  width: 100%;
}

.modal_content {
  display: none;
  border-radius: 2px;
  background: #fff;

  .is-active & {
    display: block;
  }
}
jQuery
$(function(){
  var $modal = $('.modal');
  var $inner = $('.modal_inner');
  var $btn = $('.modalBtn');
  var $close = $('.modalClose');

  // モーダルを開く処理
  $btn.on('click', function(event) {
    event.preventDefault();
    $modal.addClass('is-active');
  });

  // モーダルを閉じる処理
  $close.add($modal).on('click', function(event) {
    event.preventDefault();
    $modal.removeClass('is-active');
  });

  // コンテンツクリック時に閉じないようにする処理
  $inner.on('click', function(event) {
    event.stopPropagation();
  });
});

ページに複数のモーダルウィンドウが存在する場合

  • モーダルウィンドウテンプレートを複数用意して data属性で管理
html
<!-- モーダルを表示させるためのボタンにはclass="modalBtn"を付与 -->
<button class="modalBtn" data-modalIndex="1" type="button">モーダル表示ボタン</button>
<p>aタグでも<a class="modalBtn" data-modalIndex="2" href="#">モーダル表示</a></p>

<!-- モーダルウィンドウテンプレート1 -->
<div class="modal" data-modalIndex="1">
  <div class="modal_container">
    <div class="modal_inner">
      <div class="modal_content">
        <div>ここにモーダルで表示させたいものをいれます。</div>
        <!-- モーダルを閉じるボタンにはclass="modalClose"を付与 -->
        <button class="modalClose" type="button">閉じる</button>
        <p>aタグでも<a class="modalClose" href="#">閉じる</a></p>
      </div>
    </div>
  </div>
</div>

<!-- モーダルウィンドウテンプレート2 -->
<div class="modal" data-modalIndex="2">
  <div class="modal_container">
    <div class="modal_inner">
      <div class="modal_content">
        <div>モーダル2</div>
        <!-- モーダルを閉じるボタンにはclass="modalClose"を付与 -->
        <button class="modalClose" type="button">閉じる</button>
        <p>aタグでも<a class="modalClose" href="#">閉じる</a></p>
      </div>
    </div>
  </div>
</div>
scss
変更なし
jQeury
$(function(){
  var $modal = $('.modal');
  var $inner = $('.modal_inner');
  var $btn = $('.modalBtn');
  var $close = $('.modalClose');

  // モーダルを開く処理
  $btn.on('click', function(event) {
    event.preventDefault();
    var _modalIndex = $(this).attr('data-modalIndex') || null;

    if (_modalIndex) {
      $modal.filter('[data-modalIndex="' + _modalIndex + '"]').addClass('is-active');
    } else {
      $modal.addClass('is-active');
    }
  });

  // モーダルを閉じる処理
  $close.add($modal).on('click', function(event) {
    event.preventDefault();
    $modal.removeClass('is-active');
  });

  // コンテンツクリック時に閉じないようにする処理
  $inner.on('click', function(event) {
    event.stopPropagation();
  });
});

モーダルウィンドウ下のコンテンツをスクロールさせたくない場合

  • モーダルウィンドウが表示されているときにはその他のコンテンツを固定させる
HTML
<body>
<div class="wrapper>
~
</div>
</body>
SCSS
.wrapper {
  &.is-locked {
    position: fixed;
    width: 100%;
  }
}
JavaScript
$(function(){
  var $modal = $('.modal');
  var $inner = $('.modal_inner');
  var $btn = $('.modalBtn');
  var $close = $('.modalClose');

  var $win = $(window);
  var $wrapper = $('.wrapper');
  var scrollPosition;
  var scrollPositionLocked;

  // スクロール位置を取得
  $win.on('scroll load', function(event) {
    scrollPosition = $win.scrollTop();
  });

  // モーダルを開く処理
  $btn.on('click', function(event) {
    event.preventDefault();
    var _modalIndex = $(this).attr('data-modalIndex') || null;

    // モーダル以外を固定
    scrollPositionLocked = scrollPosition;
    $wrapper.addClass('is-locked');
    $wrapper.css({top: -scrollPosition});

    if (_modalIndex) {
      $modal.filter('[data-modalIndex="' + _modalIndex + '"]').addClass('is-active');
    } else {
      $modal.addClass('is-active');
    }

  });

  // モーダルを閉じる処理
  $close.add($modal).on('click', function(event) {
    event.preventDefault();

    // モーダル以外を固定解除
    $wrapper.removeClass('is-locked');
    $win.scrollTop(scrollPositionLocked);

    $modal.removeClass('is-active');
  });

  // コンテンツクリック時に閉じないようにする処理
  $inner.on('click', function(event) {
    event.stopPropagation();
  });
});
kitikitchen
35歳、京都在住、大阪のWeb制作会社勤務。
un-t
インターネットビジネスを中心とした企画、設計、デザイン、システム、運用、マーケティング、リサーチ等の総合的なクリエイティブファームです。
https://www.un-t.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした