4
1

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-05-02

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

商品詳細ページにコメント機能を実装しました。

全部で7回に分けて記事を投稿しています。

内容 url
第1回 モデル、マイグレーション編 https://qiita.com/sho_U/items/03108801146e65d58413
第2回 ルーティング編 https://qiita.com/sho_U/items/5c829b3060be2cce919a
第3回 コントローラー編 https://qiita.com/sho_U/items/8528f336cf0d470cd719
第4.1回 ヴュー編(一覧表示) https://qiita.com/sho_U/items/6190562270c722956547
第4.2回 ヴュー編(インプットフォーム) https://qiita.com/sho_U/items/67c2ede4fc6c605283e2
第5回 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

実装イメージとしては

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

という流れになります。

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

カスタムデータ属性とは

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

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

<div class="message">

これに、idといった属性名を持ち、値が"3"であるカスタムデータ属性を付与すると

<div class="message" data-id="3">

となります。

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

$(".message").data('id');

値を取り出すことができます。

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

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

<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">

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

$(".message[data-id="4"]")

という形で取得することができます。

では、この機能を用いて「完全削除機能」をjqueryで実装したいと思います。 
50f2d2d8971c5b089da8f1a285a1dada.png
今、仮削除状態のコメントがあります。この時、hamlでif文を通過するところのみ抜粋すると下記のようになります。

items/show.html.haml
.comment_list
 - @commentALL.each do |comment|
   .comment_Other.comment_one_block{data:{index: comment.id}}    ##1つのコメントの大枠 
     .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
{data:{index: comment.id}}

上記の記述により、hamlでカスタムデータ属性をつけることができます。今このコメントのidが"5"であるとして生成される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>

注目していただきたいのは、

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

<div class="comment_delete complete_delete" data-index="5">   ##完全削除ボタン 

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

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

comment.js
$(".comment_list").on('click','.complete_delete',function(e){
  e.preventDefault()
  var index = $(this).data("index");
  $(`.comment_one_block[data-index=${index}]`).remove();
  });

解説します。

comment.js
$(".comment_list").on('click','.complete_delete',function(e){

「完全削除ボタン」がクリックされた場合発火します。

comment.js
e.preventDefault()

「aタグのherfで指定されたURLへ遷移する」というブラウザのデフォルトの動作を停止します。

comment.js
 var index = $(this).data("index");

クリックした「完全削除ボタン」の、属性名indexのカスタムデータ属性の値(この場合 5)を変数 index  に格納する。

comment.js
  $(`.comment_one_block[data-index=${index}]`).remove();

index(この場合 5)をカスタムデータ属性に持つ、つまり「完全削除ボタン」と同じカスタムデーター属性の値を持つ、コメントの型枠を.remvoeメソッドで消し去ります。

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

「コメント仮削除」

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

動作イメージd3d40ea56c99355e1a882a1a33965dfd.gif

考え方としては、「完全削除」の時と同じように、「削除ボタン」と同じカスタムデータ属性値を持つコメント型枠に対して操作するという流れですが、「完全削除」の時は、コメント消去であったのに対し、「仮削除」では、コメントの差し替えになります。

今、以下のようなコメントがあったとします。
815f3e83dbff55bc6a85ca7e881c6731.png
そして、「削除ボタン」を押した時
0c9bdda9035a7aed7b1d9fdf9975bfcd.png
という表示に切り替えます。つまり、吹き出しの中身を入れ替えるということですね。

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

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
===================================
<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を解説します。

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

comment.js
$(".comment_list").on('click',".me_pre_delete",function(e){

削除ボタン(".me_pre_delete")がクリックしたら発火します。

comment.js
  var index = $(this).data("index");

クリックされた削除ボタンのカスタムデータ属性値を変数indexに格納する。(今回であれば"5")

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に格納されるんですね。

comment.js
content.empty();

そして、emptyメソッドで、一旦吹き出しの中身を空にします。

comment.js
content.append(PLEdelete(index));

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

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は
2d1ca105271450a18702eeedac9be615.png

この部分が格納されているわけですね。作成されたコメントのボタンにも、jqueryの動作に対応できるようカスタムデータ属性を与えています。
そして、このコメントの中身を返り値として返却し、先ほど説明したように空の状態の吹き出しに追加して、
8595e13b2c460cf23c2eeb8357e8e15.png

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

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

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

次回は、第6回〜ajax〜編となります。

4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1