モーダルウィンドウを実装したい、でもライブラリ使うほどの要件ではない。
といった時に使える自前でパパっとモーダルウィンドウの実装。
##やりたいこと
- 要素(ボタンや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();
});
});