jQueryを使って投稿フォームをモーダルで表示させる

## はじめに
Bootstrapでモーダルウィンドウを導入したときに、なぜウィンドウが表示させるのかが分からなかったので、SCSSとjQueryを使って実際に書いてみることにしました。

## 前提
Rails6
jQuery3
Bootstrap4
を使用しています。

## 参考にさせていただいた記事
ほとんどは以下の記事を参考にさせていただきました。
どの部分で画面が表示されているんだ？という部分が曖昧だったので、
「ウィンドウを表示する機能の作成」→「レイアウトの調整」
の順番に手順を分解しながら作成しました。

https://recooord.org/jquery-modal-window/

## まずは、機能を作る

「投稿する」ボタンと、ボタンを押したときに「表示する中身」を設置します。

```html:show.html
  <!--投稿するボタン-->
  <button class="modal-open btn btn-info">投稿する</button>
  <div class="modal-body">
    <div class="modal-close"><i class="fa-solid fa-circle-xmark"></i></div>
    <div class="modal-post-content">
      <p>中身</p>
    </div>
  </div>
 ```
 :::note info
 クラスの設定
 modal-open：モーダルを開くためのボタン
 modal-body：表示させるウィンドウ部分
 modal-close：モーダルを閉じるためのボタン
:::

ウィンドウを表示するために必要なCSSの記述はこちらです。

```scss:application.scss
// 初期の設定で隠しておく
.modal-body {
  visibility: hidden;
}

// クリックした際にactiveクラスを追加または削除して表示状態を変更する
.modal-body.active {
  visibility: visible;
}
```

ウィンドウを開いたときと閉じたときの設定をjsファイルに記述します。

```diff_javascript:post_modal.js
+document.addEventListener('turbolinks:load', function (){
 $(function(){
  var open = $('.modal-open'),
      close = $('.modal-close'),
      container = $('.modal-container');

// modal-openをクリックしたらactiveクラスが追加される
  open.on('click', function(){
    container.addClass('active');
    return false;
  });

// modal-closeをクリックしたらactiveクラスが取り除かれる
  close.on('click', function(){
    container.removeClass('active');
  });

// モーダル画面の外側をクリックしたらactiveクラスが取り除かれる
  $(document).on('click', function(e) {
      if(!$(e.target).closest('.modal-body').length){
        container.removeClass('active');
      }
  });
});
+});
 ```

今回はRailsでTurbolinksを使用しており、turbolinks:loadを入れないとリロードしなければ発火しない状態になっていたので、1行目の記述を追加しています。

### 「モーダル画面の外側をクリックした際」の記述について…
 
 ```javascript
 // ドキュメント全体でユーザーがクリックした要素の情報を取得してeventに格納
   $(document).on('click', function(event) {
 // closestでeventがmodal-bodyクラスを持っているか探して、lengthで見つけた要素の数を返す
      if(!$(event.target).closest('.modal-body').length){
 // !で論理否定となり、lengthが0(false)だったらactiveクラスが取り除かれる
        container.removeClass('active');
      }
  });
 ```
 クリックした箇所に、modal-bodyクラス（モーダルウィンドウの中身）が見つからない場合は、removeClassでactiveクラスを取り除く形になっています。
 

## レイアウトを調整する
次に、ウィンドウのレイアウトを調整します。

+の部分：ウィンドウが真ん中に来るように<b>位置を調整</b>しています。
+以外の追加部分：ウィンドウの背景を白にしたり、角丸にしたり、<b>細かいレイアウト調整</b>を行います。
```diff_scss:application.scss
.modal-body {
  visibility: hidden;
+  position: fixed;
+  top: 50%;
+  left: 50%;
+  transform: translate(-50%, -50%);
  border-radius: 15px;
  border: solid 1px #ccc;
  padding: 20px;
  opacity: 1;
  background-color: #fff;
}

.modal-close {
  position: absolute;
  top: 0;
  right: 0;
  font-size: 30px;
  color: #ccc;
}

.modal-body.active {
  visibility: visible;
}
```

## 背景を透過グレーにする

ウィンドウを表示させるだけだと少し見づらかったので、ウィンドウの後ろに透過グレーの背景を付けることにします。

「投稿する」ボタンの位置はどこでも良いのですが、画面全体にグレー背景を被せるため、モーダルウィンドウ自体はビューの一番下に設置しました。
また、ウィンドウの中身は投稿フォームの部分テンプレートを表示するようにします。

```diff_html:show.html
<!--投稿するボタン-->
<button class="modal-open btn btn-info">投稿する</button>
:
:
+   <div class="modal-container"></div>
    <div class="modal-body">
      <div class="modal-close"><i class="fa-solid fa-circle-xmark"></i></div>
      <div class="modal-post-content">
-        <p>中身</p>
+        <%= render 'works/form', work: @new_work %>
      </div>
    </div>
```

jsファイルにもmodal-containerの設定を追加します。

```diff_javascript:post_modal.js
document.addEventListener('turbolinks:load', function (){
  $(function(){
    var open = $('.modal-open'),
        close = $('.modal-close'),
        body = $('.modal-body'),
+       container = $('.modal-container');

    open.on('click', function(){
      body.addClass('active');
+     container.addClass('active');
      return false;
    });

    close.on('click', function(){
      body.removeClass('active');
+     container.removeClass('active');
    });

    $(document).on('click', function(event) {
        if(!$(event.target).closest('.modal-body').length){
          body.removeClass('active');
+         container.removeClass('active');
        }
    });
  });
});
```

最後に、CSSに透過グレー背景用の記述を追加します。

```diff_scss:application.scss
// 透過グレーの背景
.modal-container {
  visibility: hidden;
  position: fixed;
+  width: 100%;
+  height: 100%;
  top: 0;
  left: 0;
+  opacity: 0.4;
+  background-color: #0e1731;
}

// 中身の白いウィンドウ
.modal-body {
  visibility: hidden;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 15px;
  border: solid 1px #ccc;
  padding: 20px;
  background-color: #fff;
  opacity: 1;
}

.modal-close {
  position: absolute;
  top: 0;
  right: 10px;
  font-size: 30px;
  color: #ccc;
}

.modal-body.active,
+.modal-container.active {
  visibility: visible;
}
```

これで完成したのがこちら。

![2024-08-01モーダルJavaScript.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3762011/4cb2eef6-6204-aeb1-496e-d7497cb15acf.gif)

## おわりに
実際に書いてみて、モーダルウィンドウの仕組みはとてもシンプルだということが分かりました。
今回はこの方法でモーダルウィンドウを作成しましたが、まだ他にも書き方がありそうなので、さらに探ってみようと思います。