[ プログラミング ] jQueryでスクロールバーの出るモーダルウィンドウを作る方法

[2014/12/23]
モーダルコンテンツの下に余白が空かなかったり、スクロールバーが出る分、センタリングが微妙にずれてたりしてたのを修正しました。スクロールバーの横幅を取得するのに、スクロールバーの有るdiv要素を生成・削除しているのですが、スクロールバーが出ていない状態でスクロールバーの横幅を取得する方法ってないんですかねー?

以前、以下の記事でモーダルウィンドウを作ったのですが、これは、body部分がスクロールして、モーダルコンテンツは固定するタイプでした。

逆に、body部分は固定で、モーダルコンテンツがウィンドウより大きい場合にスクロールバーを出してスクロールできるようなモーダルウィンドウをjQueryで作ってみました。

こういうタイプのプラグインでは、Remodalというのが有名です。

デモは以下からどうぞ。

DOMO

HTML5でコーディングしています。jQueryもCSSもHTMLのheadタグ内に書いています。

HTML

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>[DEMO]jQueryでスクロールバーの出るモーダルウィンドウを作る方法</title>
<script src="js/jquery.js"></script>
<script>
$(function(){
    // スクロールバーの横幅を取得
    $('html').append('<div class="scrollbar" style="overflow:scroll;"></div>');
    var scrollsize = window.innerWidth - $('.scrollbar').prop('clientWidth');
    $('.scrollbar').hide();

    // 「.modal-open」をクリック
    $('.modal-open').click(function(){
        // html、bodyを固定(overflow:hiddenにする)
        $('html, body').addClass('lock');

        // オーバーレイ用の要素を追加
        $('body').append('<div class="modal-overlay"></div>');

        // オーバーレイをフェードイン
        $('.modal-overlay').fadeIn('slow');

        // モーダルコンテンツのIDを取得
        var modal = '#' + $(this).attr('data-target');

         // モーダルコンテンツを囲む要素を追加
        $(modal).wrap("<div class='modal-wrap'></div>");

        // モーダルコンテンツを囲む要素を表示
        $('.modal-wrap').show();

        // モーダルコンテンツの表示位置を設定
        modalResize();

         // モーダルコンテンツフェードイン
        $(modal).fadeIn('slow');

        // モーダルコンテンツをクリックした時はフェードアウトしない
        $(modal).click(function(e){
            e.stopPropagation();
        });

        // 「.modal-overlay」あるいは「.modal-close」をクリック
        $('.modal-wrap, .modal-close').off().click(function(){
            // モーダルコンテンツとオーバーレイをフェードアウト
            $(modal).fadeOut('slow');
            $('.modal-overlay').fadeOut('slow',function(){
                // html、bodyの固定解除
                $('html, body').removeClass('lock');
                // オーバーレイを削除
                $('.modal-overlay').remove();
                // モーダルコンテンツを囲む要素を削除
                $(modal).unwrap("<div class='modal-wrap'></div>");
           });
        });

        // リサイズしたら表示位置を再取得
        $(window).on('resize', function(){
            modalResize();
        });

        // モーダルコンテンツの表示位置を設定する関数
        function modalResize(){
            // ウィンドウの横幅、高さを取得
            var w = $(window).width();
            var h = $(window).height();

            // モーダルコンテンツの横幅、高さを取得
            var mw = $(modal).outerWidth(true);
            var mh = $(modal).outerHeight(true);

            // モーダルコンテンツの表示位置を設定
            if ((mh > h) && (mw > w)) {
                $(modal).css({'left': 0 + 'px','top': 0 + 'px'});
            } else if ((mh > h) && (mw < w)) {
                var x = (w - scrollsize - mw) / 2;
                $(modal).css({'left': x + 'px','top': 0 + 'px'});
            } else if ((mh < h) && (mw > w)) {
                var y = (h - scrollsize - mh) / 2;
                $(modal).css({'left': 0 + 'px','top': y + 'px'});
            } else {
                var x = (w - mw) / 2;
                var y = (h - mh) / 2;
                $(modal).css({'left': x + 'px','top': y + 'px'});
            }
        }

    });
});
</script>

<style>
body {
    margin:10px;
    padding:10px;
    border:10px solid #ddd;
}

.lock {
    overflow:hidden;
}

.modal-content {
    position:relative;
    display:none;
    width:50%;
    margin:30px;
    padding:10px 20px;
    border:2px solid #aaa;
    background:#fff;
}

.modal-content p {
    margin:0;
    padding:0;
}

.modal-overlay {
    z-index:1;
    display:none;
    position:fixed;
    top:0;
    left:0;
    width:100%;
    height:120%;
    background-color:rgba(0,0,0,0.75);
}

.modal-wrap {
    z-index:2;
    display:none;
    position:fixed;
    top:0;
    left:0;
    width:100%;
    height:100%;
    overflow:auto;
}

.modal-open {
    color:#00f;
    text-decoration:underline;
}

.modal-open:hover {
    cursor:pointer;
    color:#f00;
}

.modal-close {
    color:#00f;
    text-decoration:underline;
}

.modal-close:hover {
    cursor:pointer;
    color:#f00;
}
</style>
</head>
<body>
<a data-target="con1" class="modal-open">リンク1</a>
<a data-target="con2" class="modal-open">リンク2</a>
<a data-target="con3" class="modal-open">リンク3</a>
<a data-target="con4" class="modal-open">リンク4</a>
<a data-target="con5" class="modal-open">リンク5</a>

<div id="con1" class="modal-content">
    <p><a href="./">リンク1の内容です。</a>・・・</p>
    <p><a class="modal-close">閉じる</a></p>
</div>
<div id="con2" class="modal-content">
    <p><a href="./">リンク2の内容です。</a>・・・</p>
    <p><a class="modal-close">閉じる</a></p>
</div>
<div id="con3" class="modal-content">
    <p><a href="./">リンク3の内容です。</a>・・・</p>
    <p><a class="modal-close">閉じる</a></p>
</div>
<div id="con4" class="modal-content">
    <p><a href="./">リンク4の内容です。</a>・・・</p>
    <p><a class="modal-close">閉じる</a></p>
</div>
<div id="con5" class="modal-content">
    <p><a href="./">リンク5の内容です。</a>・・・</p>
    <p><a class="modal-close">閉じる</a></p>
</div>
<p>
クリックしてみてください。ウィンドウより大きいモーダルウィンドウにはスクロールバーが出ていると思います。<br>
</p>
<p><a href="http://coolwebwindow.com/jquery-lab/archives/352">&lt;&lt; 記事に戻る</a></p>
</body>
</html>

モーダルコンテンツの横幅はCSSで50%指定、marginで30pxの余白を取っています。モーダルコンテンツの縦のサイズがウィンドウより小さい場合は中央に配置するようにしています。モーダルコンテンツの横幅はpxで指定することも可能ですが、モーダルコンテンツの横幅がウィンドウより大きくなった場合はデザインを考慮していません。

ハマってしまったところとしては、モーダルコンテンツ部分をクリックしても、モーダルウィンドウが解除されてしまったので、39行目のところで、モーダルコンテンツをクリックしても無効化しています。これは、親要素(.modal-wrap)のイベントを子要素が引き継いでしまう仕様のようで、それを解除するには「e.stopPropagation();」を使用すれば良いようです。