21
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

un-T factory! XAAdvent Calendar 2017

Day 10

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

Last updated at Posted at 2017-12-10

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

##やりたいこと

  • 要素(ボタンや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_y: 20px;

// モーダルウィンドウの左右の余白
$modal_padding_x: 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_x;
  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_y auto;
  width: auto;
}

.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();
  });
});
21
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
21

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?