【フリマアプリ】商品詳細ページのコメント機能（第５回）〜jquery編〜

　某スクールにおいて、チーム開発で、フリーマーケットアプリを作成中であり、使用した技術について公開しています。
※初学者のため、ミスや認識違いが多々あると思いますがご了承ください。

# 商品詳細ページにコメント機能を実装しました。
全部で7回に分けて記事を投稿しています。

    |内容             |                  url   |
|:-----------------|------------------:|:-----------------------------:|
| 第１回             |       モデル、マイグレーション編 |    https://qiita.com/sho_U/items/03108801146e65d58413   |
| 第２回         |           ルーティング編 |     https://qiita.com/sho_U/items/5c829b3060be2cce919a |
| 第３回|コントローラー編|https://qiita.com/sho_U/items/8528f336cf0d470cd719|
|第４.1回|ヴュー編(一覧表示）|https://qiita.com/sho_U/items/6190562270c722956547|
|第４.2回|ヴュー編(インプットフォーム）|https://qiita.com/sho_U/items/67c2ede4fc6c605283e2|
|第５回|jquery編|https://qiita.com/sho_U/items/d72b60114f76380d05f6|
|第6回|ajax編|https://qiita.com/sho_U/items/caed9b1471e63d43dd3a|

〜コード全文〜https://qiita.com/sho_U/items/310ac5b653bdfcb99a2c

#jqueryについて
jqueryを用いて実装する部分は、**「コメント仮削除」「コメント完全削除」**した場合となります。「コメント作成」「コメント復元」についてはajaxを用いるため。次回に解説いたします。

まずは、よりシンプルな実装である**「コメント完全削除」**から説明いたします。

##「コメント完全削除」

###動作イメージ

![b7717e9403c63e63b98be14e67834fd5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/ca35d029-2959-e784-dedc-8c3c0eddf347.gif)

###実装イメージとしては

```
1. 「完全に削除する」をクリックしたら、jqueryが発火
2. クリックされた「完全に削除する」ボタンと紐づいているコメントの型枠を消去する。
```
という流れになります。

ポイントとしては、クリックしたボタンとコメントの型枠をどのように紐づけるかということです。
その方法として、**「カスタムデータ属性」** というものを使います。

###カスタムデータ属性とは
カスタムデータ属性とはHTML5から追加されたマークアップ仕様で、classやid、hrefといった様々な属性のように、オリジナルの属性を作ることができます。
カスタムデータ属性は **「data-〇〇」** という形で記述されます。〇〇には属性名として任意の文字列を入れます。

例えば、下記のようなdivタグがあるとします。

```html
<div class="message">
```
これに、**id**といった属性名を持ち、値が**"3"**であるカスタムデータ属性を付与すると

```html
<div class="message" data-id="3">
```
となります。

こうすることで、jqueryによりカスタムデータ属性の値を**dataメソッド**で取得することができます。
例えば、下記のように記載することで

```javascript
$(".message").data('id');
```
値を取り出すことができます。

```javascript
var ID = $(".message").data('id');
console.log(ID);
//     => "3"
```

また、例えば下記のようにそれぞれ値が異なるカスタムデータ属性をもつ、messageクラスがあるとします。

```html
<div class="message" data-id="1">
<div class="message" data-id="2">
<div class="message" data-id="3">
<div class="message" data-id="4">
<div class="message" data-id="5">
```

ここで、値が**"４"**であるカスタムデータ属性をもつ、messageクラスをノードとして取得したい場合は、

```javascript
$(".message[data-id="4"]")
```
という形で取得することができます。

では、この機能を用いて「完全削除機能」をjqueryで実装したいと思います。　
<img width="706" alt="50f2d2d8971c5b089da8f1a285a1dada.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/a9331efe-5fbe-5acc-3896-d016fb16861d.png">
今、仮削除状態のコメントがあります。この時、hamlでif文を通過するところのみ抜粋すると下記のようになります。

```ruby:items/show.html.haml
.comment_list
 - @commentALL.each do |comment|
   .comment_Other.comment_one_block{data:{index: comment.id}}    ##１つのコメントの大枠 
     .comment_user_name    ##ニックネームの表示
       = comment.user.nickname
     .comment_content_other   ##青い吹き出し
       出品者によりこのコメントは削除されました。
       .comment_restore{data:{index: comment.id}}    ##復元ボタン
         = link_to "復元する",restore_comment_path(comment.id)
       .comment_delete.complete_delete{data:{index: comment.id}}    ##完全削除ボタン    
         = link_to '完全に削除する', comment_path(comment.id) ,method: :delete
```

```ruby
{data:{index: comment.id}}
```
上記の記述により、hamlでカスタムデータ属性をつけることができます。今このコメントのidが"5"であるとして生成されるhtmlを確認してみます。

```html
<div class="comment_list>
         (略）
  <div class="comment_Other comment_one_block" data-index="5">
    <div class="comment_user_name">
      ちゃ
    </div>
    <div class="comment_content_other">
      出品者によりこのコメントは削除されました。
      <div class="comment_restore" data-index="5">
        <a href="/comments/8/restore">復元する</a>
      </div>
      <div class="comment_delete complete_delete" data-index="5">
        <a rel="nofollow" data-method="delete" href="/comments/5">
          完全に削除する
        </a>
      </div>
    </div>
  </div>
</div>
```
注目していただきたいのは、

```html
<div class="comment_Other comment_one_block" data-index="5">  ##１つのコメントの大枠 

<div class="comment_delete complete_delete" data-index="5">   ##完全削除ボタン 
```
コメントの大枠と、完全削除ボタンのindexという属性名のカスタムデータ属性の値が同じとなっています。これは、hamlの記載で値を どちらも**comment.id**　にしているため、両方とも**コメントのid**が、設定されるわけです。コメントのidは重複することはないため、当然他のコメントを表示する部分とカスタムデータ属性の値が重複することはありません。

では、実際にjqueryの記載を確認してみましょう。

```javascript:comment.js
$(".comment_list").on('click','.complete_delete',function(e){
  e.preventDefault()
  var index = $(this).data("index");
  $(`.comment_one_block[data-index=${index}]`).remove();
  });
```
解説します。

```javascript:comment.js
$(".comment_list").on('click','.complete_delete',function(e){
```
**「完全削除ボタン」**がクリックされた場合発火します。

```javascript:comment.js
e.preventDefault()
```
「aタグのherfで指定されたURLへ遷移する」というブラウザのデフォルトの動作を停止します。

```javascript:comment.js
 var index = $(this).data("index");
```
クリックした「完全削除ボタン」の、属性名indexのカスタムデータ属性の値（この場合　５）を変数　**index**　　に格納する。

```javascript:comment.js
  $(`.comment_one_block[data-index=${index}]`).remove();
```
index（この場合　５）をカスタムデータ属性に持つ、つまり「完全削除ボタン」と同じカスタムデーター属性の値を持つ、コメントの型枠を.remvoeメソッドで消し去ります。

このプロセスを経て、瞬時にコメントを消去するわけです。

##「コメント仮削除」
次にコメント仮削除機能について説明します。

###動作イメージ![d3d40ea56c99355e1a882a1a33965dfd.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/1ae12dfd-1ad2-2495-9e8e-0e64a964e76b.gif)
考え方としては、「完全削除」の時と同じように、「削除ボタン」と同じカスタムデータ属性値を持つコメント型枠に対して操作するという流れですが、「完全削除」の時は、コメント**消去**であったのに対し、「仮削除」では、コメントの**差し替え**になります。

今、以下のようなコメントがあったとします。
<img width="696" alt="815f3e83dbff55bc6a85ca7e881c6731.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/3fc8ec67-c760-03ba-9d73-0677b2aa6d69.png">
そして、「削除ボタン」を押した時
<img width="698" alt="0c9bdda9035a7aed7b1d9fdf9975bfcd.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/d41b699c-599d-d41e-aa3c-6dc8c13e85c2.png">
という表示に切り替えます。つまり、吹き出しの中身を入れ替えるということですね。

以下がjqueryのコードとなります。

```javascript:comment.js
// ===================================
// 仮削除表示用
// ===================================
function PLEdelete(index){
  var html = 
  `
  出品者によりこのコメントは削除されました。
  <div class="comment＿restore" data-index=${index}>
    <a href="/comments/${index}/restore">復元する</a>
  </div>
  <div class="comment_delete complete_delete" data-index=${index}>
    <a rel="nofollow" data-method="delete" href="/comments/${index}">完全に削除する</a>
  </div>
  `
return html;
};

// ===================================
// 自分のコメントを仮削除した場合
// ===================================
$(".comment_list").on('click',".me_pre_delete",function(e){
  e.preventDefault()
  var index = $(this).data("index");
  var content =  $(`.comment_one_block[data-index=${index}]`).find(".comment_content");
  content.empty();
  content.append(PLEdelete(index));
});
// ===================================
// 他人のコメントを仮削除した場合
// ===================================
$(".comment_list").on('click',".other_pre_delete",function(e){
  e.preventDefault()
  var index = $(this).data("index");
  var content =  $(`.comment_one_block[data-index=${index}]`).find(".comment_content_other");
  content.empty();
  content.append(PLEdelete(index));
});
```
解説します。

まず最初に操作する対象のhtmlを確認します。自分のコメントと他人のコメントで色や形を変えるため表示されるhtmlは一部クラス名が異なっています。それぞれの、htmlの概略を以下のようにします。(コメントのidは5とする）

```html:html
===================================
自分のコメントを表示する場合のhtml
===================================
<div class="comment_lists>
　<div class="comment_Me commen_one_block" data-index="5">
　　<div class="comment_content">
　　　　<!-- comment_contentというクラス名は、ピンク色の吹き出し（自分用）のcssが当たっている。 -->
　　 　　<!-- コメントの内容、投稿時間のhtmlは略 -->
    <div class="comment_delete me_pre_delete" data-index="5">
      <a rel="nofollow" data-method="patch" href="/comments/12">削除する</a>
    </div>
                <!-- 以下略 -->
===================================
他人のコメントを表示する場合のhtml
===================================
<div class="comment_lists>
  <div class="comment_Other commen_one_block" data-index="5">  
  <div class="comment_content_other">
    <!-- comment_content_otherというクラス名は、ブルーの吹き出し（他人用）のcssが当たっている。 -->
　　 <!-- コメントの内容、投稿時間のhtmlは略 -->
    <div class="comment_delete other_pre_delete" data-index="5">
      <a rel="nofollow" data-method="patch" href="/comments/10">削除する</a>
    </div>
                <!-- 以下略 -->
```
自分のコメントと他人のコメントの違いは、吹き出しの形を形成するためのクラスの名前が、**「comment_content」**と**「comment_content_other」**と違いがあり、また、**自分のコメントの「削除ボタン」**は、**「me_pre_delete」**、**他人のコメントの「削除ボタン」**には**「other_pre_delete」**というクラス名のdivタグに、それぞれ囲われていることがわかります。また、「comment_Me」「comment_Other」クラスはそれぞれコメントを形成する一番外側の枠で、前者はニックネームが吹き出しの右側に、後者はニックネームが左側に表示するようにしています。

以上のことを念頭において、jqueryを解説します。

###自分のコメントの削除ボタンがクリックされた場合

```javascript:comment.js
$(".comment_list").on('click',".me_pre_delete",function(e){
```
削除ボタン（".me_pre_delete"）がクリックしたら発火します。

```javascript:comment.js
  var index = $(this).data("index");
```
クリックされた削除ボタンのカスタムデータ属性値を変数indexに格納する。（今回であれば"5"）

```javascript:comment.js
  var content =  $(`.comment_one_block[data-index=${index}]`).find(".comment_content");
```

まず　$(`.comment_one_block[data-index=${index}]`)で、クリックされた「削除ボタン」と同じカスタムデータ属性値を持つ**comment_one_block（コメントの一番外枠）**を取得します。そして、**findメソッド**で、子要素である**"comment_content（コメントの吹き出し）"**を取得して、**変数content**に格納します。結果として、クリックした「削除ボタン」を包含する吹き出しが**content**に格納されるんですね。

```javascript:comment.js
content.empty();
```
そして、**emptyメソッド**で、一旦吹き出しの中身を空にします。

```javascript:comment.js
content.append(PLEdelete(index));
```
content(吹き出し）に**appendメソッド**で**(PLEdelete(index))**を追加します。
**(PLEdelete(index))**とは、index（今回であれば"5")を引数に**PLEdeleteメソッド**を呼び出した時の返り値ですね。
では、**PLEdeleteメソッド**をみてみましょう。

```javascript:comments.js
function PLEdelete(index){  //今、indexには、削除ボタンのカスタムデータ属性値である"5"が格納されている。
  var html = 
  `
  出品者によりこのコメントは削除されました。
  <div class="comment＿restore" data-index=${index}>
    <a href="/comments/${index}/restore">復元する</a>
  </div>
  <div class="comment_delete complete_delete" data-index=${index}>
    <a rel="nofollow" data-method="delete" href="/comments/${index}">完全に削除する</a>
  </div>
  `
return html;
};
```
ハッシュリテラルを用いて、**仮削除状態時のhtml**をそのまま**変数html**に格納しています。

つまり変数htmlは
<img width="449" alt="2d1ca105271450a18702eeedac9be615.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/3a8854b9-e3b3-ea93-992a-f568ef759f05.png">

この部分が格納されているわけですね。作成されたコメントのボタンにも、jqueryの動作に対応できるようカスタムデータ属性を与えています。
そして、このコメントの中身を返り値として返却し、先ほど説明したように空の状態の吹き出しに追加して、
<img width="686" alt="8595e13b2c460cf23c2eeb8357e8e15.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547556/663ff889-37c7-5d55-23e5-2ecb438bccf8.png">

結果として、上記のコメントが完成するわけです。

自分以外のコメントも吹き出しの形が違いますが、表示する中身は同じであるため、**(PLEdelete(index))**を呼び出します。

以上で「完全削除」と「仮削除」の実装が完了しました。

###次回は、第６回〜ajax〜編となります。
https://qiita.com/sho_U/items/caed9b1471e63d43dd3a