モーダル作成で知っておきたいこと[HTML/CSS/jQuery]

#はじめに
ひさびさにhtml/css/jqueryでモーダル画面を作成する機会があり、いろいろと学びがあったのでメモ。
基本的なモーダル画面を作成し、そこからいろんなケースに対応するために改良していきます。


#基本編
まず、ボタンをクリックするとチェックボックスが配列されているモーダルウィンドウが表示される、というものをつくっていきます。
![modal9.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/c7d5d677-29fb-481c-ae9c-88a4816acf1c.gif)


###html
```html:index.html
<main>
　　<div class="panel">
        <a class="c-btn js-open-modal" href="#">モーダル表示</a>
        <div class="modal-panel-back disable">
            <div class="modal-panel">
                <div class="modal-panel-ttl">さらに絞る必要がある場合は、下記からせんたくしてください。</div>
                <div class="check-item-area row">
                    <div class="col-sm-6 check-item">
                        <label><input type="checkbox" class="check-init"><span>AAA</span></label>
                    </div>
　　　　　　　　　　　（・・・省略・・・）
                    <div class="col-sm-6 check-item">
                        <label><input type="checkbox" class="check-init"><span>KKK</span></label>
                    </div>
                    </div>
                    <div class="btn-area">
                        <a class="c-btn js-close-modal" href="#">設定</a>
                    </div>
                </div>
            </div>
        </div>        
    </main>    
</body>
</html>
```
htmlはこんな感じ。
ポイントはモーダル表示したときの背景グレー領域(modal-panel-back)とモーダル領域(modal-panel)を作成すること。

###css
```css:style.css
.panel {
  width: 680px;
  max-width: 90%;
  margin: 100px auto;
  padding: 20px;
  -webkit-box-shadow: 0 1px 2px rgba(0,0,0,0.2);
          box-shadow: 0 1px 2px rgba(0,0,0,0.2);
  background-color: white;
  text-align: center;
}
.modal-panel-back {
  position: fixed;
  width: 100%;
  height: 100%;  
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(200,200,200,.5);
}
.modal-panel-back.disable {
  display: none;
}
.modal-panel {
  width: 600px;
  background-color: white;
  padding: 30px;
  margin: 100px auto;
}
```
cssはこんな感じ。
`.modal-panel-back`
グレー背景を画面いっぱいに広げるためにposition:fixedにする。position:absoluteだと親のどこかにposition:relativeが指定されているとそこが基準になり配置されるため、画面いっぱいに広がらない。かといって、ルートのタグ直下にグレー背景用のタグを用意するのはjquery操作がはいるのでめんどくさい。
`.modal-panel-back.disable`
モーダル領域は隠す
`.modal-panel`
モーダル領域はシンプルに、横幅widthを指定して、margin: oopx autoで中央に表示する。


###script
```html:index.html
<script>
   $(function(){
       $('.js-open-modal').on('click', function(){
           $(this).next().removeClass('disable');
       });
       $('.js-close-modal').on('click', function(e){
           e.preventDefault();
           $(this).parents('.modal-panel-back').addClass('disable');
       })
   })
</script>
```
scriptはこんな感じ。
「モーダル表示」をクリックでdisableを削除しモーダル領域を表示。「設定」をクリックするとdisableを追加してモーダルを消す。
e.preventDefault()で明示的にaタグのイベントをキャンセルしています。これがないと「設定」をクリックしたときにhrefで指定したURLに飛ぼうとします。


基本編のモーダル表示はこんな感じです。ここからいろいろなケースを想定して改善していきます。

##ケース１：グレー背景クリックでモーダルを閉じたい
普通のモーダルウィンドウって、背景のグレー領域をクリックすると閉じるのが普通ですよね。なのでそのように実装していきましょう。WEB制作でもここらへんは当たり前に実装しておきたいです。

純粋に考えると、下記コードをscriptに追加しようと考えます。下記コードを追加して、背景部をクリックしてみてください。

```html:index.html
<script>
   $(function(){
       $('.modal-panel-back').on('click', function(){
            $(this).addClass('disable');
        })
   })
</script>
```
![modal2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/a15c3e7b-c87c-b800-d59b-4dec6d27c2bb.gif)

きちんと消えました。
次にモーダルを表示したあと、チェックボックスをクリックしてみてください。
![modal3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/f1378640-7d1b-5bfd-b6f2-8353ec6118b1.gif)

チェックボックスをクリックすると、モーダル領域が消えました。
ちょっと焦りますが、これはイベントバブリングという現象です。

#####イベントバブリング
子要素でイベントが発生したときに、親やさらに先の親にも同じイベントが発生するというのがイベントバブリングです。詳しい説明は割愛しますが、要は`.modal-panel`のモーダル領域でクリックすると、親要素である`.modal-panel-hide`のグレー背景もクリックされるということです。
これを回避するために、下記のコードを追加しましょう。これで`.modal-panel`のモーダル領域で発生したイベントは親要素`.modal-panel-hide`に伝搬しなくなります。

```html:index.html
<script>
   $(function(){
       $('.modal-panel').on('click', function(e){
            e.stopPropagation();
        })
   })
</script>
```

これで背景部をクリックしてモーダルを閉じる動作が実装できました。


##ケース２：画面がスクロールする場合
ここまでの例では、「モーダル表示」ボタンが配置されている画面はスクロールしませんでした。ただ、大概のウェブサイトはスクロールするので、その状況にも対応していきましょう。
まず、cssファイルに下記を追加しましょう。これでスクロールするようになります。

```css:style.css
html, body {
  height: 1500px; //←追加
}
```
![modal10.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/41ea8966-e7c5-8c4f-3f1c-3cb49668c442.gif)


この状態で「モーダル表示」ボタンを表示してみます。
![modal5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/97843661-8f15-9c5b-a048-0d219c10d4de.gif)

モーダル表示しているのに元画面のスクロールが働いてしまっています。これではブサイクなのでスクロールできないようにしましょう。モーダル表示したときはbodyのスクロールが働かないようにコードを追加します。

```html:index.html
<script>
   $(function(){
       $('.js-open-modal').on('click', function(){
            $(this).next().removeClass('disable');
            $('body').addClass('modal-open');　　//追加
        });
        $('.js-close-modal').on('click', function(e){
            e.preventDefault();
            $(this).parents('.modal-panel-back').addClass('disable');
            $('body').removeClass('modal-open');　//追加
        })
        $('.modal-panel-back').on('click', function(){
            $(this).addClass('disable');
            $('body').removeClass('modal-open');　//追加
        })
   })
</script>
```
```css:style.css
body.modal-open {
    overflow: hidden;
}
```
モーダル表示したときはbodyのスクロールが効かないようにしました。
これでモーダルを表示してみましょう。
![modal6.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/4a72dcb7-e287-2914-de28-0e5ab3b16d8e.gif)

スクロールしないようにできました。
・・が、よくよく見るとモーダル表示したとき「モーダル表示」ボタンを配置している領域が微妙に右側に動いてしまっています。今回の例はかなりシンプルでわかりにくいですが、普通のサイトでこれが再現するとかなり気持ち悪いです。なので修正します。
下記コードを追加してください。

```css:style.css
body.modal-open {
    overflow: hidden;
    padding-right: 15px; //追加
}
```
padding-rightを追加しました。微妙に右に動いてしまうのは、スクロールの領域がなくなった分、全体が右側に移動してしまうからです。なので動かないようにスクロールの幅分paddingを与えてやります。これで気持ち悪さはなくなります。
![modal7.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/fa3e69b6-588a-3685-5f67-7c4d93418ace.gif)

##ケース３：モーダル画面がアクティブウィンドウからはみ出る場合
ここまでのモーダル画面は、アクティブウィンドウからはみ出していませんでしたが、はみ出してしまう場合を考えます。
チェックボタンの要素を増やし、モーダル画面がアクティブウィンドウからはみ出すようにしましょう。

これの状態でモーダル画面を表示するとスクロールしないのでしたのほうを確認することができません。
![modal11.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/005591a9-96c2-8f4b-5d90-ca22f3361e8d.gif)

モーダルがスクロールするように修正します。

`.modal-panel-back`のcssにoverflow-y:scrollを指定します。

```css:style.css
.modal-panel-back {
  position: fixed;
  width: 100%;
  height: 100%;  
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(200,200,200,.5);
  overflow-y: scroll; //追加
}
```

この状態でモーダルを表示するとスクロールできるようになります。
![modal12.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/187747/a2a03785-9dfe-0e38-23a7-7428a0de0a2f.gif)



#おわりに
モーダル表示といえど意外と考えないといけないことが多かったので、まとめてみました。
だれかのお役に立てればうれしいです。