１つのページ内に異なるカウントダウンを作成するDay12

本日もお疲れ様です。現役テックキャンプ受講生のアプリ開発Day１２です。最近昼夜逆転気味なり、更新も遅い時間になりがちです。いつかのタイミングで生活リズムを戻さねばと思ってます。

今日はjsによるカウントダウンを作成しました。現在、ディスカッションのためのWebアプリを作成しているのですが、ディスカッションするためのルームを一定時間経過後、自動的に閉じるように設定しようと思ってます。そのためのカウントダウンを作成していたのですが、１つのページで複数の異なるカウントダウンを表示させる方法が検索でもなかなか出てこなかったので、ここでアウトプットします。

イメージはこちらをご覧ください
https://gyazo.com/87f473586453759e904aa1ebfaa9629c

今回はRoomを複数作成し、それぞれにカウントダウンを表示させる方法について説明します。
結論はカウントダウンを作る記述をforループ内に記述すると簡単にできます。

# DBにdeadline（締切）カラムを追加
締切の日付を登録するためのdatetime型のカラムを追加します。
```ruby:migrate-file
class CreateRooms < ActiveRecord::Migration[6.1]
  def change
    create_table :rooms do |t|
      t.string :name, null: false
      t.datetime :deadline
      t.timestamps
    end
  end
end
```
# コントローラーにdeadline登録のための記述をする
今回、全てのルームは作成した瞬間から、三日後に自動で閉じるようにします。従って、Time.nowで現在時間を取得し、Time.now+3.daysで三日後の日付を取得します。
```ruby:room_controller
  def create
    @room = Room.new(room_params)
    if @room.save
      redirect_to room_path(@room)
    else
      render 'new'
    end
  end

  private
  def room_params
    params.require(:room).permit(:name).merge(user_ids: [current_user.id],deadline:Time.now+3.days)
  end
```
# htmlをjsが利用しやすいように変更
主に、IDの追加やClass名を編集し、後程のjsで利用しやすいようにします。
まず、コントローラーでRoom全てを取得し、htmlに出力します。
```ruby:room_controller
def index
  @rooms = Room.all
end
```
```html:roomを一覧表示
<% @rooms.each do |room| %>
　　<%= room.name%>
　　残り時間:
　　　　<div class="deadline" id="<%=room.id%>" style="display:none;">
    <%=room.deadline%> # この記述でRoomのデットラインを取得
  </div>
　　　　<span id="hour<%=room.id%>"></span>時間　
　　　　<span id="min<%=room.id%>"></span>分
　　　　<span id="sec<%=room.id%>"></span>秒
<% end %>
```
３つのspanは後程、jsで処理した出力結果を入れるための箱になります。

# js
順番に確認していきます。その前に大変お世話になったサイト↓

https://pote-chil.com/js_timer/

### ①ページが読み込まれた段階で、jsを起動するための記述をします。
```javascript:timebom
function countdown(){
}

window.addEventListener('load',countdown); // これでロード時にcountdown関数が起動。 
```
### ②今の時間を取得し、deadlineのClassを取得します。今回取得するdeadlinesはRoomの数を数えるための取得でもあるので、あえてidではなくclassで取得します。
```javascript:timebom
function countdown(){
  const now = new Date();//今の時間
  const deadlines = document.getElementsByClassName("deadline");//deadlineを配列で取得
}
```
### ③forループでRoomの数だけ、残り時間を計算するようにします。ここで難しい点は２つ。
・"new Date（記述）"で文字情報の日付をデータタイム形式の日付に変更します。
・”(new Date(deadlines[i].textContent) - now)”で締切と現時間との差を計算しています。計算結果はミリ秒（1000m秒＝1秒）になります。
ついでに、”getAttribute("id")”でhtml仕込んでいたIDも取得します。
```javascript:timebom
function countdown(){
  const now = new Date();//今の時間
  const deadlines = document.getElementsByClassName("deadline");//deadlineを取得
  for(let i = 0; i < deadlines.length; i++){
    const differ = (new Date(deadlines[i].textContent) - now);//今回は3日後まであと何ミリ秒か
    const differ_id = deadlines[i].getAttribute("id")
  };
}
```
### ④秒、分、時間を計算します。
```javascript:timebom、forループ内
（中略）
const sec=Math.floor(differ/1000)%60;
const min=Math.floor(differ/1000/60)%60;
const hour=Math.floor(differ/1000/60/60);
（中略）

//ちなみに日を測りたい場合は
//const hour=Math.floor(differ/1000/60/60)%24;
//const day=Math.floor(differ/1000/60/60/24);
```
### ⑤htmlにjsで処理した結果を入力します。padStartは1桁になった場合に頭に０をつけてくれるものです。
```javascript:timebom、forループ内
//（中略）
document.getElementById("hour"+differ_id).textContent=String(hour).padStart(2,"0"); //一桁になった時0がつくように
document.getElementById("min"+differ_id).textContent=String(min).padStart(2,"0")
document.getElementById("sec"+differ_id).textContent=String(sec).padStart(2,"0")
```
### ⑥最後に2秒ごとに時間の表示を変更させます。
```javascript:timebomの全体像
  function countdown(){
  const now = new Date();//今の時間
  const deadlines = document.getElementsByClassName("deadline");//deadlineを取得
  // console.log(deadlines[0].getAttribute("id"))
  for(let i = 0; i < deadlines.length; i++){
    const differ = (new Date(deadlines[i].textContent) - now);//3日後まであと何ミリ秒か
    const differ_id = deadlines[i].getAttribute("id")
    const sec=Math.floor(differ/1000)%60;
    const min=Math.floor(differ/1000/60)%60;
    const hour=Math.floor(differ/1000/60/60);

    document.getElementById("hour"+differ_id).textContent=String(hour).padStart(2,"0"); //一桁になった時0がつくように
    document.getElementById("min"+differ_id).textContent=String(min).padStart(2,"0")
    document.getElementById("sec"+differ_id).textContent=String(sec).padStart(2,"0")
  };
  setTimeout(countdown,1000);//forループの外側に記述
}

window.addEventListener('load',countdown);
```
# 参考サイト

https://pote-chil.com/js_timer/

https://qiita.com/satopin/items/04157574e976517c5d9b

https://techacademy.jp/magazine/28774

https://qiita.com/amamamaou/items/25e8b4e1b41c8d3211f4

https://itsakura.com/js-getattribute