モーダルウィンドウを作ってみた話

#やりたかったこと
ajaxを使ってCRUD機能を持ったアプリを作っていたところ、入力や削除などでちょっとモダンなウィンドウ作りたくなったので、
某SNS風のモーダルウィンドウを見よう見まねで作ってみました。

![03a42cd5500e6db1909a3fe765224fae.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257615/9cd86743-4c61-32eb-4336-8f7a92b4638d.png)

[デモサイト](https://okauend.github.io/my_modal_window.github.io/)

#どうやったか
SPAで要素を逐次追加するのではなく、予めhtmlファイルにモーダルウィンドウの要素を作成し、クラスを付与しておきます。
CSSのプロパティの一つである、要素の透明度を指定する`opacity`を使うことで、クラスの有無で表示非表示の表現を。
javascriptでクラス要素を操作することで、表示と非表示の切り替えを行うようにしました。

###HTML
####コード
```html:index.html
        <section id="main">
            //コンテンツ
        </section>
        <section id="modal" class = "modal--is_hidden">
            <div class="modal__body">
                <div class="modal__header modal__base">
                    <h2 class="modal__titletext"></h2>
                    <button class="modal__close">閉</button>
                </div>
                <div class="modal__item modal__base"></div>
                <div class="modal__footer modal__base">
                    <button class = 'modal_buttom modal__Cancel'>キャンセル</button>
                    <button class = 'modal_buttom modal__Submit'></button>
                </div>
            </div>
            <div class="modal__back modal__back--is_hidden"></div>
        </section>
```
####解説
最初の`section`で囲まれた箇所が、このページのコンテンツを記載するところです。
idが`modal`の`section`が、モーダルウィンドウ部分です。
次にその中身ですが、

```html:index.html
            <div class="modal__body">
　　　　　　　　　//中略//
            </div>
            <div class="modal__back modal__back--is_hidden"></div>
```
クラスが`modal__body`に表示したいコンテンツのパーツを挿入する部分で、
クラスが`modal__back`がそれ以外の背景部分です。
![img3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/257615/dc50b52b-2df7-eda0-f7d7-bd815d5496c3.png)
背景の部分は、モーダルウィンドウを表示中に画面全体を黒く半透明を覆い被せるUIとしての働きと、
この部分をクリックするとモーダルウィンドウを閉じる、といった2つの役割を持っています。
閉じる機能自体は、キャンセルボタンと右上の閉ボタンとか、複数の箇所でできるようにしておくべきかなと思ってます。

次に`modal__body`の中身をさらっと

```html:index.html
        <div class="modal__header modal__base">
            <h2 class="modal__titletext"></h2>
            <button class="modal__close">閉</button>
        </div>
        <div class="modal__item modal__base"></div>
        <div class="modal__footer modal__base">
            <button class = 'modal_buttom modal__Cancel'>キャンセル</button>
            <button class = 'modal_buttom modal__Submit'></button>
        </div>
```

`header`、`item(body)`、`footer`の3つに分割しています。
`header`の中の`h2`や、`footer`の`button`の一つに文字を書いていませんが、
これはjavascript側で動的に変更できるようにし、汎用的に使えるようにするためにそうしています。
`item(body)`も箱だけを作っておき、中身はjavascript側でDOMを作り内容を変更できるようにしてあります。

###CSS
####コード

```css:style.css
.modal__back--is_hidden
{
    opacity: 0;
    pointer-events: none;
}

.modal--is_hidden
{
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s ease-out;
}
```
`***--is_hidden`には`opacity`プロパティの値を0にし、設定し完全な透明状態にしています。
ただ、その状態だと透明になっただけで、モーダルウィンドウに対しての反応は残り続けているので、
クリックしてもモーダルウィンドウより下にある要素に対してのクリックを行うことはできません。
そこで`pointer-events`プロパティを使い値を`none`に設定することで、
クリックのターゲットから外すことで、下にあるものをクリックできるようにします。

また、`opacity`だけでは表示と非表示のアニメーション行われず、すぐに表示・非表示になるので、
`transition`を使い、`0.2s`の極短時間ではありますがアニメーションを指定することで、ウィンドウが徐々に消えるようにしました。

ただ、`display:none`でも非表示にすることができますが、その場合は徐々に半透明にするなどのアニメーションをいれることができないため、`opacity`を今回使用致しました。
（実は、keyframeとanimationを使えば同じ事が可能ですが、今回は`transition`を選択しました。）

```css:style.css
.modal__base
{
    margin:0;
    padding:0;
    justify-content: center;
    text-align: center;
    align-items: center;
    display: flex;
}

.modal__header
{
    height: 45px;
    background-color: #bedcff;
    border-radius: 5px 5px 0 0;
    flex-direction: row;
}

.modal__item
{
    min-height: 100px;
    background-color: #ffffff;
    flex-direction: column;
}

.modal__footer
{
    height: 50px;
    background-color: #ffffff;
    border-top:1px solid #bdbdbd;
    border-radius:0 0 5px 5px ;
    justify-content: flex-end;
}
```
`header`、`item(body)`、`footer`のデザインです。
`modal__base`でフレックボックスを指定し、小要素が中央表示になるよう指定してあります。

###javascript
####コード
```javascript:index.js
    function openModalWindow()
    {
       //非表示にしているクラスを取り除き、表示状態にする
        document.querySelector('#modal').classList.remove('modal--is_hidden');
        document.querySelector('.modal__back').classList.remove('modal__back--is_hidden');
    
       //モーダルウィンドウを閉じるためのイベントを追加する
        document.querySelector('.modal__back').addEventListener('click',closeModalWindow,false);
        document.querySelector('.modal__Cancel').addEventListener('click',closeModalWindow,false);
        document.querySelector('.modal__close').addEventListener('click',closeModalWindow,false);
    }
```
こちらはモーダルウィンドウを表示させるための処理です。
htmlのclassで非表示状態にしている`***--is_hidden`を、`remove`メソッドで除去しています。
また、背景、キャンセルボタン、右上の閉じるボタンに対してモーダルウィンドウを閉じるイベントを追加しています。

CSS側で`transition`をしてしていますので、javascript

```javascript:index.js
    function closeModalWindow()
    {    
        //モーダルウィンドウを非表示に
        document.querySelector('#modal').classList.add('modal--is_hidden');
        document.querySelector('.modal__back').classList.add('modal__back--is_hidden');
    
       //モーダルウィンドウを閉じるためのイベントを削除
        document.querySelector('.modal__back').removeEventListener('click',closeModalWindow,false);
        document.querySelector('.modal__Cancel').removeEventListener('click',closeModalWindow,false);
        document.querySelector('.modal__close').removeEventListener('click',closeModalWindow,false);
    }
```
こちらは逆にモーダルウィンドウを非表示させるための処理です。
ほとんど表示する側の反対のことやってて、特になにかあるわけではないですね(´・ω・`)

```javascript:index.js
        //タイトルを設定する
        document.querySelector('.modal__titletext').appendChild(document.createTextNode('ほげほげウィンドウ'));
```
タイトルや子要素を追加する方法は、`appendChild`を使いました。
文字列の追加も、`innerHTML`などを使わずに'createTextNode'を用いてTextノードを生成して追加してます。


#やってみた感想
某SNS風のデザインでしたが、一からモーダルウィンドウを作ってみてhtmlの構成の仕方、cssの設計、javascriptのコーディング等、色々学ぶことができて大変よい機会となりました。

###次の課題
次に同じモーダルウィンドウを作るとしましたら、Vueなどのフレームワークを使って作り、色々なフレームワークでの作り方などを習得していきたいなと思っております。