[JavaScript]タスク完了機能の作成
タスク完了機能を作成しています。機能としては下記の通り。
- 未完了のタスクをチェックして取り消し線を引く
- 完了済のタスクは未完了に戻すことができる
- 完了状態が変わるときはモーダルでメッセージを表示させる
まだエラーが多く、とてもお見せできる状態ではないのですが、JavaScriptは触れたことがあまりなかったので、今回使ったコードをまとめていきます。
1. イベントリスナーの設定
document.addEventListener("click", async (event) => {
-
document.addEventListener("click", ...)
で ページ内のどこかがクリックされたときに処理を、以下の処理を実行するという意味。 -
async
を付けているのは、非同期処理(fetch
を使用)を行うため。
非同期処理
プログラムは通常上から下に実行する。
しかし、サーバにデータ送信したり、ファイルを読み込むのは時間がかかるため、待ってる間にほかの処理を行うことが非同期処理。
2. クリックされた要素を取得
const target = event.target.closest(".task-checkbox");
-
event.target
は クリックされた要素 を指す。 -
.closest(".task-checkbox")
で、クリックされた要素が.task-checkbox
クラスを持っているか、親要素にそのクラスがあるかを探す。 - これにより、
.task-checkbox
クラスを持つ チェックボックスやボタンのクリックのみ処理 するようになる。
今回だとビューに下記のように設定されてるので、このチェックアイコンをクリックしたときのみ動作する。
<% if task.user == current_user %>
<span class="task-checkbox" data-task-id="<%= task.id %>">
<i class="fa-solid fa-circle-check <%= 'completed-icon' if task.completed %>"></i>
</span>
<% else %>
<span class="task-checkbox" data-task-id="<%= task.id %>">
<i class="fa-solid fa-circle-check"></i>
</span>
<% end %>
3. タスクIDの取得
if (!target) return;
const taskId = target.dataset.taskId;
-
.dataset.taskId
で data属性(data-task-id="123"
など)からタスクIDを取得 。 -
taskId
が存在しない場合、エラーログを出力して処理を中断。
4. モーダルを「処理中」にして表示
const messageModal = document.getElementById("messageModal");
const messageText = document.getElementById("messageText");
messageText.textContent = "処理中...";
-
messageModal
は、タスク完了処理の ステータスメッセージを表示するモーダル。 -
messageText.textContent = "処理中..."
で、モーダルのテキストを 処理中に変更。
5. サーバーにタスク完了リクエストを送信
const response = await fetch(`/tasks/${taskId}/complete`, {
method: "PATCH",
headers: {
"X-CSRF-Token": document.querySelector("meta[name='csrf-token']").content,
"Content-Type": "application/json"
},
credentials: "same-origin"
});
-
fetch
を使って PATCHリクエストを送信(タスクの完了/未完了を切り替える)。 -
"X-CSRF-Token"
を メタタグから取得 し、CSRF対策。 -
"credentials: same-origin"
は クッキーを送信 するために必要。 querySelector
は、指定したHTMLの要素を取得。
fetch
を使って PATCHリクエストを送信
fetch
とは?
📌 fetch
は、JavaScriptでHTTPリクエストを送るための関数 。
-
GET
リクエストならfetch("/example")
のように書くだけでデータを取得できる。 -
POST
やPATCH
などのリクエストを送るには、method
を指定 。
📌 PATCHリクエストとは?
HTTPのリクエストには GET
、POST
、PATCH
、DELETE
などのメソッドがある。
(Railsでもよく出てくる内容!)
メソッド | 役割 |
---|---|
GET | データを取得する |
POST | データを新規作成する |
PATCH | データの一部を更新する |
DELETE | データを削除する |
今回の PATCH
リクエストでは、タスクの「完了/未完了」の状態を変更するため、PATCH
メソッドを使用する。
📌 送信するURL
fetch(`/tasks/${taskId}/complete`, { ... })
-
${taskId}
には、完了したいタスクのIDが入る。 -
/tasks/123/complete
のようなURLにPATCH
することで、サーバー側でタスクの完了状態を更新。
"X-CSRF-Token"` を メタタグから取得 し、CSRF対策。
"X-CSRF-Token": document.querySelector("meta[name='csrf-token']").content
📌 CSRF(Cross-Site Request Forgery)とは?
CSRF攻撃とは、ユーザーが 意図せず処理を実行されてしまう攻撃 のこと。
例えば、以下のような悪意のあるページを開いたとすると…
<img src="https://example.com/tasks/123/complete" />
もしCSRF対策がなければ、このページを見ただけで 勝手にタスクが完了状態に変更されてしまう 可能性がある。
📌 CSRF対策の仕組み
Railsでは、CSRF対策として トークン(csrf-token
) をHTMLのメタタグに埋め込む。
<meta name="csrf-token" content="ランダムなトークン値">
JavaScript側では、このトークンを取得して、リクエストの headers
に追加することで、CSRF対策を行う。
document.querySelector("meta[name='csrf-token']").content
これは、HTMLの <meta>
タグから csrf-token
を取得するコード。
📌 なぜCSRFトークンが必要なのか?
Railsのサーバーは、リクエストの中に X-CSRF-Token
が含まれているかどうかをチェックし、不正なリクエスト(CSRF攻撃)を防ぐ ようになる。
トークンがないと、Railsは「このリクエストは安全ではない」と判断し、処理を拒否する。
"credentials: same-origin"
は クッキーを送信 。
credentials: "same-origin"
このオプションは、リクエストに認証情報(クッキー)を含めるかどうかを設定する もの。
📌 クッキーとは?
クッキーは、ユーザーのログイン情報などを保存する仕組み 。
例えば、RailsのDeviseを使っている場合、ログイン状態は クッキーに保存 される。
credentials: "same-origin"
が必要なのか?
📌 なぜ デフォルトでは、fetch
は クッキーを送信しない。
つまり、credentials
を指定しないと、Railsは「このリクエストを誰が送っているのかわからない」と判断して、ログイン状態を認識できなくなる 可能性あり。
credentials の値 |
役割 |
---|---|
"omit" |
クッキーを送信しない(デフォルト) |
"same-origin" |
同じオリジン(サイト)ならクッキーを送信する |
"include" |
クロスオリジン(他サイト間通信)でもクッキーを送る |
ここで same-origin
を指定することで、Railsにログインしているユーザーとしてリクエストを送れる ようになる。
まだ途中ですが…あまりにも多いので今日はここまで!
また、明日調べます!
参考文献
Discussion