【コメント欄】+-の比率でコメントの色・大きさが動的に変わるUIの実装

コメント欄にup、downボタンとそれぞれの比率のバーを設置。

そのコメントがどれだけ支持されているかが一目で分かるようなUIを作成したので
何を考えていたのか、どう実装したかを解説。

↓実装した先のサイト
[なろう廃人のすすめ](https://www.narou-osusume.com/)

![コメントクラス付け替え (1) (1).gif](https://qiita-image-store.s3.amazonaws.com/0/145707/b8a57748-9c3c-e0f1-be4d-f6c5135a5076.gif)

![screenshot-2018-07-28-215743.PNG](https://qiita-image-store.s3.amazonaws.com/0/145707/f33edc36-5853-8089-0350-e134dca611d1.png)


#何を考えていたのか
##1、コメントがどれだけ支持されているかを可視化

up,downの比率により、読者の支持を可視化。
コメントを読むべきか読まなくて良いかが分かるようにして
**見る人の脳内コストの削減**をしたかった。

up,downの比率によってコメントのクラスを付け替えて
色、大きさが変わるようにしてパッと入ってくるようにしている。

##2、画面から反応が返ってくるようにしてサイトを楽しくする
画面から反応が返ってくることによって
**参加している感**を出して、
**【参加したくなるサイト】**を目指した。

#処理の流れ
1、ユーザがボタンを押す
2、jqueryでカウント数を増やす・ボタンを押せなくする
3、ajaxでrailsのコントローラを呼んでコメントのupカラムを+1する
4、ajaxの処理がsuccessで返ってきたときは、バーのwidthをアニメーションで変更する
5、画面からカウント数の比率を取得して、コメントのクラスを付け替える

#実際の処理
##html部分
【ポイント】
・up_down_rateを取得、comment-level-1~5のクラス分けをしてコメントの見た目を変更
・バーの部分は灰色の背景、青い前面の２つを用意して、青い前面のwidthをup_down_rateの%により制御

```haml:_card.html.haml
.card.my-2.mx-0.border-0
  .text-muted.small.p-2
    名前: #{comment.name}
  -# up,downの比率を取得
  - up_down_rate = ((comment.up.to_f) / (comment.up.to_f + comment.down.to_f)) * 100
  -# 比率によりクラスを分ける
  - if    90 <= up_down_rate
    .p-2.comment.comment-level-5
      #{comment.comment}
  - elsif 70 <= up_down_rate  && up_down_rate < 90
    .p-2.comment.comment-level-4
      #{comment.comment}
  - elsif 50 <= up_down_rate  && up_down_rate < 70
    .p-2.comment.comment-level-3
      #{comment.comment}
  - elsif 30 <= up_down_rate  && up_down_rate < 50
    .p-2.comment.comment-level-2
      #{comment.comment}
  - else
    .p-2.comment.comment-level-1
      #{comment.comment}
  .container.mb-0.mt-3.pr-1
    .row.mx-auto.d-flex.justify-content-end.comment-bottom-content
      %p.up-contents.mb-0
        %a.comment-rating-button.comment-rating-button-up.d-inline-flex.justify-content-center.font-weight-bold{} +
        %br
        %span{class: "comment-count comment-count-up mb-0"}
          - if comment.up != nil
            #{comment.up}
          - else
            0
      -# 0で割れないので、分母が0の時で場合分け
      - if (comment.up.to_f + comment.down.to_f) != 0
        .comment-rating-bar.justify-content-center.m-2.mt-3
          -# up, downの比率で青いバーの長さを決定する
          .comment-rating-bar-up{style: "width: #{( (comment.up.to_f) / (comment.up.to_f + comment.down.to_f) ) * 100}%;"}
      - else
        .comment-rating-bar.justify-content-center.m-2
          -# up, downがどちらも0の時は50%にする
          .comment-rating-bar-up{style: "width: 50%;"}
      %p.down-contents.mb-0
        %a.comment-rating-button.comment-rating-button-down.d-inline-flex.justify-content-center.font-weight-bold{} -
        %br
        %span{class: "comment-count comment-count-down mb-0"}
          - if comment.down != nil
            #{comment.down}
          - else
            0
      .comment-id{style: "display:none"}
        #{comment.id}
```

##1、ユーザがボタンを押す

###コード前半部分 jqueryでcss弄る部分
・up,downボタンのクラスをpushedに付け替えて、動かないようにする
・upのcountを+1する。

```javascript:application.js
  $(".comment-rating-button-up").click(function(){
    //upを押された時に、upとdownどちらも動かないようにクラスを付け替える  TODO セレクターがダサいから直す。
    $(this).removeClass("comment-rating-button-up").addClass("comment-rating-button-up-pushed");
    var down_button = $(this).parent().next().next().children(".comment-rating-button-down");
    down_button.removeClass("comment-rating-button-down").addClass("comment-rating-button-down-pushed");

    var old_up_count = parseInt($(this).siblings(".comment-count-up").text());
    $(this).siblings(".comment-count-up").text(old_up_count + 1);
```

###コード中盤 ajax送信部分
コメントのidをhiddenでhtmlに埋め込んであるので、
それをjqueryで取得してajaxで送る。

```javascript:application.js
    $.ajax({
        url: "comment_count_up",
        data: { id : parseInt($(this).parent().siblings(".comment-id").text()) },
        dataType: "html",
```

```ruby:controller.rb
  def comment_count_up
      @comment = Comment.find_by(id: params[:id])
      if @comment.up == nil
        @comment.update(up: 1)
      else
        @comment.update(up: @comment.up + 1)
      end
  end
```

```ruby:routes.rb
  #resourcesの上に書く
  get "novels/comment_count_up"
```

###コード後半 コメントのクラス付け替え、バーの長さ変更

・up_down_rateを最初に取得
・rateに応じてcomment-levelを決定
・元のcomment-levelのクラスを削除して、上で定義したcomment-levelをつける

```javascript:application.js

        success: (data) => {
          var up_count   = parseInt($(this).parents(".comment-bottom-content").children(".up-contents").children(".comment-count-up").text());
          var down_count = parseInt($(this).parents(".comment-bottom-content").children(".down-contents").children(".comment-count-down").text());
          var sum_count = up_count + down_count
          var up_down_rate = ((up_count/sum_count)*100)
          // 青いバーのwidthをanimeteを使って変更
          $(this).parent().next().children(".comment-rating-bar-up").animate({width:up_down_rate + "%"},200);

          // rateに応じてcomment_levelを定義
          switch(true) {
              case 90 <= up_down_rate:
                  var comment_level = "comment-level-5";
                  break;
              case 70 <= up_down_rate && up_down_rate < 90 :
                  var comment_level = "comment-level-4";
                  break;
              case 50 <= up_down_rate && up_down_rate < 70 :
                  var comment_level = "comment-level-3";
                  break;
              case 30 <= up_down_rate && up_down_rate < 50 :
                  var comment_level = "comment-level-2";
                  break;
              case up_down_rate < 30 :
                  var comment_level = "comment-level-1";
                  break;
              default:
                  alert("0より小さいです");
          }
          //前方一致でクラスを削除  参考：jQueryの.removeClass()で「特定の文字列で始まるclass」をすべて削除する
          //comment_levelのクラスを付け加える
          $(this).parents(".card").children(".comment").removeClass(function(index, className) {
              return (className.match(/\bcomment-level-\S+/g) || []).join(' ');
          }).addClass(comment_level);

          console.log("正常にup完了")
        },
```

###javascriptコード全体（upボタンを押された場合）

```javascript:application.js
//upボタン押された時
$(document).on('turbolinks:load',function(){
  $(".comment-rating-button-up").click(function(){
    //upを押された時に、upとdownどちらも動かないようにクラスを付け替える  TODO セレクターがダサいから直す。
    $(this).removeClass("comment-rating-button-up").addClass("comment-rating-button-up-pushed");
    var down_button = $(this).parent().next().next().children(".comment-rating-button-down");
    down_button.removeClass("comment-rating-button-down").addClass("comment-rating-button-down-pushed");

    var old_up_count = parseInt($(this).siblings(".comment-count-up").text());
    $(this).siblings(".comment-count-up").text(old_up_count + 1);
    $.ajax({
        url: "comment_count_up",
        data: { id : parseInt($(this).parent().siblings(".comment-id").text()) },
        dataType: "html",
        success: (data) => {
          //TODO function get_up_down_rateに切り出ししたい
          var up_count   = parseInt($(this).parents(".comment-bottom-content").children(".up-contents").children(".comment-count-up").text());
          var down_count = parseInt($(this).parents(".comment-bottom-content").children(".down-contents").children(".comment-count-down").text());
          var sum_count = up_count + down_count
          var up_down_rate = ((up_count/sum_count)*100)
          $(this).parent().next().children(".comment-rating-bar-up").animate({width:up_down_rate + "%"},200);

          switch(true) {
              case 90 <= up_down_rate:
                  var comment_level = "comment-level-5";
                  break;
              case 70 <= up_down_rate && up_down_rate < 90 :
                  var comment_level = "comment-level-4";
                  break;
              case 50 <= up_down_rate && up_down_rate < 70 :
                  var comment_level = "comment-level-3";
                  break;
              case 30 <= up_down_rate && up_down_rate < 50 :
                  var comment_level = "comment-level-2";
                  break;
              case up_down_rate < 30 :
                  var comment_level = "comment-level-1";
                  break;
              default:
                  alert("0より小さいです");
          }
          //前方一致でクラスを削除  参考：jQueryの.removeClass()で「特定の文字列で始まるclass」をすべて削除する
          $(this).parents(".card").children(".comment").removeClass(function(index, className) {
              return (className.match(/\bcomment-level-\S+/g) || []).join(' ');
          }).addClass(comment_level);

          console.log("正常にup完了")
        },
        error: function(data) {
          console.log('upをつけるのに失敗しています');
        }
    });
  });
});
```

#CSS
ポイント
・押した後のボタンはpointer-events: none;を入れて押せなくする。

```scss:novels.scss

.comment-rating-button {
  display: block;
  width: 30px;
  height: 30px;
  border-radius: 15px;
  font-size: 25px;
  padding-top: 5px;
  margin-bottom: 1px;
  // ユーザーがボタンのテキストをハイライトできないようにする
  -webkit-user-select:none;
  -moz-user-select:none;
  -ms-user-select:none;
  user-select:none;
}
.comment-rating-button-up{
  background: #73DCFF;
  box-shadow: 0 2px 0 #2a82a3;
  color: white !important;
  &:hover{
    background-color: #2a82a3 !important;
  }
}
.comment-rating-button-up-pushed{
  background-color: #2a82a3 !important;
  box-shadow: 0 2px 0 #2a82a3;
  color: white !important;
  pointer-events: none;
  &:hover{
    background-color: #2a82a3 !important;
  }
}



.comment-rating-button-down{
  background: #BDBDAE;
  box-shadow: 0 3px 0 #79796A;
  color: white !important;
  &:hover{
    background-color: #79796A !important;
  }
}
.comment-rating-button-down-pushed{
  background-color: #79796A !important;
  box-shadow: 0 3px 0 #79796A;
  color: white !important;
  pointer-events: none;
  &:hover{
    background-color: #79796A !important;
  }
}

.comment-rating-bar{
  border-radius: 20px;
  background-color: #DFDFD0;
  height: 15px;
  width: 150px;
  .comment-rating-bar-up{
    border-radius: 20px;
    background-color: #73DCFF;
    height: 15px;
    width: 100px;
  }
}

.comment-count{
  font-size: 12px;
  margin-left: 8px;
}
.comment-count-up{
  color: #5DB2CD !important;
}
.comment-count-down{
  color: #99998C !important;
}

```

```scss:comments.scss
// up_down_rate = 90%~
.comment-level-5{
  font-size: 150%;
  font-weight: bold;
  color: red;
}
// up_down_rate = 70~90%
.comment-level-4{
  font-size: 130%;
  font-weight: bold;
}
// up_down_rate = 50~70%
.comment-level-3{
  font-weight: bold;
}
// up_down_rate = 30=50%
.comment-level-2{
}
// up_down_rate = ~ 30%
.comment-level-1{
  color: #777;
}
```