html・css js・jquery

モーダルやハンバーガーメニューを開いたときに絶対に背景を動かないようにするコード(iOSも対応)

モーダルやハンバーガーメニューを開いたときに、背景を絶対に動かしたくないとき、ありますよね。

スマホや、特にiOsではbodyhtmlタグにoverflow: hidden;を設定しても動いてしまうことがあるので、それだと対応できない…というときに使えるコードです。

私も先方さんからモーダルの背景を動かしたくないと要望があった際にはこのコードを使用しています。

今回はモーダルの例ですが、開くボタンや閉じるボタンを任意のクラスに変更すれば、ハンバーガーメニューでも使えるコードです。

また、今回はhtmlscroll-behavior: smooth;が設定されている前提のコードを書いていますが、もしなければjsのそのコードに関する部分は削除して問題ありません。

では、早速行ってみましょう!

まずはコードから。

See the Pen Untitled by えり (@anyxfbuk-the-scripter) on CodePen.

汎用性を高めるためにかなりシンプルな見た目にしているので、各々で用途に合わせてデザインは調整してください。

背景が固定されているのが確認できればOKです。

解説

html

<a data-target="modal01" class="js-modal-open">モーダル開くボタン</a>
<div class="js-modal" id="modal01">
  <div class="js-modal-inner">
    <p>モーダルの中身モーダルの中身モーダルの中身モーダルの中身モーダルの中身モーダルの中身モーダルの中身モーダルの中身</p>
    <button type="button" class="js-modal-close">閉じる</button>
  </div>
</div>

まずモーダルを開くボタンにはjs-modal-openクラスをつけて、開きたいモーダルのdata-targetを設定しておきます。(今回はmodal01

モーダルにはjs-modalクラス、その中にjs-modal-innerクラスをつけた親要素を作って中身を入れ、閉じるボタンにはjs-modal-closeクラスをつけます。今回はボタンタグで作成していますが、js-modal-closeクラスがついていれば何の要素でもOKです。

css

.js-modal {
  display: none;
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  text-align: center;
  overflow: auto;
  z-index: 10001;
  cursor: pointer;
}
.js-modal-bg {
  display: none;
  position: fixed;
  top: 0;
  right: 0;
  left: 0;
  bottom: 0;
  background: rgba(0, 0, 0, 0.8);
  z-index: 10000;
  cursor: pointer;
}
.js-modal-inner {
  background: #fff;
  width: 90%;
  margin: 30px auto;
}

js-modal

  • display: none;: モーダルはデフォルトで非表示です。jsを使用して表示/非表示を切り替えます。
  • position: fixed;: モーダルは固定され、ページをスクロールしても常に画面の同じ位置に表示されます。
  • top: 0;, right: 0;, bottom: 0;, left: 0;: モーダルが画面全体を覆うように、四隅すべてを画面の端に揃えます。
  • width: 100%;: モーダルの幅は画面全体をカバーします。
  • text-align: center;: モーダル内のテキストや要素の中央揃え。なくてもOK
  • overflow: auto;: モーダル内のコンテンツが画面を超える場合、スクロール可能になります。
  • z-index: 10001;: モーダルは他の要素の上に表示。
  • cursor: pointer;: クリック可能。

今回は.js-modalz-indexで上、.js-modal-bgが下という位置関係になっているので、.js-modal-bgをクリックすることができません。なので、.js-modalをクリックしてもモーダルが閉じるような挙動になっているため、cursor: pointer;を入れています。ただし、.js-modal-inner部分はクリックしても閉じないようにjsで制御しているため、動き的には問題ないかなと。

js-modal-bg

  • display: none;: 背景もデフォルトで非表示です。JavaScriptで表示を切り替えます。
  • position: fixed;: 背景も固定され、モーダル同様に画面全体に表示されます。
  • top: 0;, right: 0;, left: 0;, bottom: 0;: 背景は画面全体を覆います。
  • background: rgba(0, 0, 0, 0.8);: 半透明の黒い背景が適用され、モーダルの背後に表示されます。色は各々調整してください。
  • z-index: 10000;: モーダルの背後にあるため、z-indexはモーダルよりも小さい値(10000)が設定されています。
  • cursor: pointer;: 背景部分もクリック可能であることを示します。

js-modal-inner

背景色やwidthmarginは今回用に適当につけているだけなので、各々で適宜変更してください。特に必須のcssはありません。

js

コードの流れを見ていきましょう。

1. $(function() {...})

$(function() {

ドキュメントが完全に読み込まれた後に実行される関数。おまじない。書いとけばOK

2. $(".js-modal-open").click(function() {...})

    $(".js-modal-open").click(function() {

クラス .js-modal-open がクリックされたときにモーダルを開く処理を行う。

3. $("html").css("scroll-behavior", "auto");

        $("html").css("scroll-behavior", "auto"); // スムーズスクロールを一時的に無効化

スムーススクロール(scroll-behavior: smooth;)が設定されている場合、一時的に無効化します。これをしておかないとモーダルを閉じるときの動きが変になるので…

もし最初からhtmlにスムーススクロールを設定していない場合は書かなくてOKです。

4. $("body").append('<div class="js-modal-bg"></div>');

        $("body").append('<div class="js-modal-bg"></div>'),

モーダルの背景(黒い半透明部分)を動的に生成して、<body>要素の末尾に追加しています。jsで生成するのでhtmlには書かなくてOK

5. $(".js-modal-bg").fadeIn("slow");

        $(".js-modal-bg").fadeIn("slow");

.js-modal-bg(背景)をゆっくりフェードインで表示します。slowの代わりに数字を入れれば秒数を指定することもできますよ。

6. var e, o = "#" + $(this).attr("data-target");

        var e, o = "#" + $(this).attr("data-target");
  • クリックされた要素(this)から、data-target属性に指定された値を取得し、変数 o に保存します。これは、開くべきモーダルのIDを指定しています。もしモーダルではなくハンバーガーメニューの場合、ここは不要です。
  • e には現在のスクロール位置($(window).scrollTop())が保存されます。モーダルを開いたときに現在のスクロール位置を記録し、閉じたときに元の位置に戻すために使います。

7. $("body , html").css({...});

        e = $(window).scrollTop();
        $("body , html").css({
            position: "fixed",
            width: "100%",
            top: -1 * e
        }),

ここが今回の肝!このコードで背景固定をしてます。

モーダルが表示されている間、bodyhtml 要素のスクロールを固定します。具体的には、以下のようにスタイルを設定しています。

  • position: fixed;: スクロールを固定し、ページが動かないようにします。
  • width: 100%;: 横幅が100%になるように設定します。
  • top: -1 * e;: スクロールした位置がずれないように、ページの表示位置を元のスクロール位置に固定します(e は前に記録したスクロール位置です)。

position: fixed;を使用することで、overflow: hidden;だと効かないスマホ等でも、背景を固定することができます。

8. $(o).fadeIn("slow");

        $(o).fadeIn("slow"),

取得したモーダル要素をフェードインで表示します。ここもslowの代わりに数字を入れれば秒数指定できます。

9. $(".js-modal-bg, .js-modal-close").off().click(function() {...});

        $(".js-modal, .js-modal-bg, .js-modal-close").off().click(function() {

背景やモーダルを閉じるためのボタン(.js-modal-close)をクリックしたときに、モーダルを閉じる処理を実行します。off()を使って以前に設定されたイベントハンドラを解除し、クリックイベントを上書きします。

10. $(o).fadeOut("slow", function() {...});

            $(o).fadeOut("slow", function() {
                // 背景のスクロールを元に戻す
                $("body, html").css({
                    position: "",
                    width: "",
                    top: ""
                });
                $(window).scrollTop(e); // 元のスクロール位置に戻す
        
                $(".js-modal-bg").fadeOut("slow", function() {
                    $(".js-modal-bg").remove();
                });

モーダルをフェードアウトで非表示にします。フェードアウトが完了したら、以下の処理が行われます。

  • $("body, html").css({...});: モーダルを閉じた後、bodyhtmlpositiontop スタイルをリセットして、ページを通常通りスクロール可能に戻します。
  • $(window).scrollTop(e);: 記録しておいたスクロール位置 e にページを戻します。
  • $(".js-modal-bg").fadeOut("slow", function() {...});: 背景をフェードアウトし、フェードアウトが完了したら .js-modal-bg 要素を削除します。

11. setTimeout(function() {...}, 1000);

                // 0.5秒後に scroll-behavior を追加
                setTimeout(function() {
                    $("html").css("scroll-behavior", "");
                }, 1000);
            });
        }),

モーダルが閉じた0.5秒後に、scroll-behavior を元に戻してスムーズスクロールを再び有効にします。この遅延は、モーダルが完全に閉じられてからスムーススクロールを復帰させるためです。すぐに戻すといったんページが上に戻ってから再度元の位置に戻るというバグっぽい動きになっちゃうので…

最初からhtmlにスムーススクロールを指定していなければここは不要です。

12. $(".js-modal-inner").on("click", function(e) {...});

        $(".js-modal-inner").on("click", function(e) {
            e.stopPropagation()
        })
    })
})

モーダルの内側(.js-modal-inner)をクリックしてもモーダルが閉じないように、e.stopPropagation() を呼び出してイベントの伝播を止めています。

全体コード

$(function() {
    $(".js-modal-open").click(function() {
        $("html").css("scroll-behavior", "auto"); // スムーズスクロールを一時的に無効化
        $("body").append('<div class="js-modal-bg"></div>'),
        $(".js-modal-bg").fadeIn("slow");
        var e, o = "#" + $(this).attr("data-target");
        e = $(window).scrollTop();
        $("body , html").css({
            position: "fixed",
            width: "100%",
            top: -1 * e
        }),
        $(o).fadeIn("slow"),
        $(".js-modal, .js-modal-bg, .js-modal-close").off().click(function() {
            $(o).fadeOut("slow", function() {
                // 背景のスクロールを元に戻す
                $("body, html").css({
                    position: "",
                    width: "",
                    top: ""
                });
                $(window).scrollTop(e); // 元のスクロール位置に戻す
        
                $(".js-modal-bg").fadeOut("slow", function() {
                    $(".js-modal-bg").remove();
                });

                // 0.5秒後に scroll-behavior を追加
                setTimeout(function() {
                    $("html").css("scroll-behavior", "");
                }, 1000);
            });
        }),
        $(".js-modal-inner").on("click", function(e) {
            e.stopPropagation()
        })
    })
})

まとめ

モーダルやハンバーガーメニューで使える、背景固定用のコードでした。

position: fixed;を使用しているところが肝ですね。ぜひ使ってみてください。

もしハンバーガーメニュー用にも実装してほしい等要望があればコード書こうと思います。

もしわからないことあれば質問ください!

記事にしてほしい・解説してほしい内容のリクエストもお待ちしています。

プログラミングに興味がある方へ

プログラミングに興味があるけど踏み出すのに勇気がいる…
始めてみたけど進め方がわからない、誰かに質問したい…
ネットの教材は高くて手が出ない…
案件を取ってみたけど納品までたどり着けない…

家庭教師の経験を活かし、そんな方のお手伝いをしたいと思っています。

30万もする悪質で高額なコンテンツマーケティングに騙される前に、相談してみませんか?
相談はもちろん無料です。

こちらから、ぜひお気軽にお問い合わせください。
お仕事のご相談や記事にしてほしい内容のリクエストもお待ちしています!

-html・css, js・jquery