🐈

[JavaScript]タスク完了機能の作成

2025/02/11に公開

タスク完了機能を作成しています。機能としては下記の通り。

  • 未完了のタスクをチェックして取り消し線を引く
  • 完了済のタスクは未完了に戻すことができる
  • 完了状態が変わるときはモーダルでメッセージを表示させる

まだエラーが多く、とてもお見せできる状態ではないのですが、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.taskIddata属性(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") のように書くだけでデータを取得できる。
  • POSTPATCH などのリクエストを送るには、method を指定

📌 PATCHリクエストとは?

HTTPのリクエストには GETPOSTPATCHDELETE などのメソッドがある。
(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攻撃とは、ユーザーが 意図せず処理を実行されてしまう攻撃 のこと。

https://www.ubsecure.jp/blog/2024csrf

例えば、以下のような悪意のあるページを開いたとすると…

<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にログインしているユーザーとしてリクエストを送れる ようになる。


まだ途中ですが…あまりにも多いので今日はここまで!
また、明日調べます!


参考文献

https://wa3.i-3-i.info/word11432.html
https://zenn.dev/qnighy/articles/772f632af595aa?utm_source=chatgpt.com
https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch
https://qiita.com/kawaz/items/1e51c374b7a13c21b7e2

Discussion