JavaScript によるフォームの送信
ユーザーが HTML フォームを送信する場合、例えば送信ボタンをクリックすると、ブラウザーは HTTP リクエストを送信してフォーム内のデータを送信します。しかし、この宣言的な手法ではなく、ウェブアプリでは、フォーム送信を期待するエンドポイントにプログラムでデータを送信するために、 fetch() などの JavaScript API を使用することがあります。この記事では、この手法が重要なユースケースである理由と、その方法について説明します。
なぜ JavaScript を使用してフォームデータを送信するのか
標準の HTML フォーム送信は、フォームデータの送信に関するこの記事で説明されているように、データが送信された URL を読み込みます。つまり、ブラウザーウィンドウが全ページ読み込みでナビゲートされるということです。
しかし、多くのウェブアプリ、特にプログレッシブウェブアプリや単一ページアプリは、サーバーからデータをリクエストし、ページの関連部分を更新するために JavaScript API を使用しており、ページ全体を読み込むオーバーヘッドを避けています。
このため、これらのウェブアプリケーションがフォームデータを送信しようとする場合、ユーザーからの入力の収集のみに HTML フォームを使用し、データ送信には使用しません。ユーザーがデータを送信しようとすると、アプリケーションがコントロールを引き継ぎ、 fetch() などの JavaScript API を使用してデータを送信します。
JavaScript フォーム送信の問題
ウェブアプリがフォームデータを送信するサーバーエンドポイントがウェブアプリ開発者の制御下にある場合、開発者はフォームデータを任意の方法で送信できます。例えば、 JSON オブジェクトとして送信できます。
しかし、サーバーエンドポイントがフォームの送信を期待している場合、ウェブアプリはデータを具体的な方法でエンコードする必要があります。例えば、データがテキストのみの場合、 URL エンコード方式でキーと値の組が掲載されているリストを作成し、 Content-Type を application/x-www-form-urlencoded として送信します。フォームにバイナリーデータが含まれている場合、 multipart/form-data コンテンツタイプを使用して送信する必要があります。
FormData インターフェイスは、データのエンコード処理をこのように行います。この記事の残りの部分では、 FormData の概要を簡単に説明します。詳細は、 FormData オブジェクトの使い方ガイドをご覧ください。
FormData オブジェクトを手動で構築
    FormData オブジェクトは、オブジェクトの append() メソッドを追加したいすべてのフィールドについて呼び出し、フィールドの名前と値を設定することによって構築できます。値は、テキストフィールドの場合は文字列、バイナリーフィールドの場合は Blob (File オブジェクトを含む)となります。
次の例では、ユーザーがボタンをクリックすると、フォーム送信という形でデータを送信します。
async function sendData(data) {
  // FormData インスタンスを構築
  const formData = new FormData();
  // テキストフィールドを追加
  formData.append("name", "Pomegranate");
  // ファイルを追加
  const selection = await window.showOpenFilePicker();
  if (selection.length > 0) {
    const file = await selection[0].getFile();
    formData.append("file", file);
  }
  try {
    const response = await fetch("https://example.org/post", {
      method: "POST",
      // FormData インスタンスをリクエスト本体として設定
      body: formData,
    });
    console.log(await response.json());
  } catch (e) {
    console.error(e);
  }
}
const send = document.querySelector("#send");
send.addEventListener("click", sendData);
- 
最初に新しい、空の FormDataオブジェクトを構築します。
- 
次に、 append()を 2 回呼び出し、FormDataオブジェクトに 2 つのアイテムを追加します。テキストフィールドとファイルです。
- 
最後に、 fetch()API を使用してPOSTリクエストを行い、リクエスト本体としてFormDataオブジェクトを設定します。
Content-Type ヘッダーを設定する必要がないことに注意してください。 FormData オブジェクトを fetch() に渡すと、正しいヘッダーが自動的に設定されます。
FormData オブジェクトと <form> の関連付け
    送信するデータが現実の <form> から来る場合は、フォームを FormData コンストラクターに渡すことで、 FormData インスタンスを生成することができます。
HTML で <form> 要素を宣言しているとします。
<form id="userinfo">
  <p>
    <label for="username">名前を入力してください:</label>
    <input type="text" id="username" name="username" value="Dominic" />
  </p>
  <p>
    <label for="avatar">アバターを選択してください</label>
    <input type="file" id="avatar" name="avatar" required />
  </p>
  <input type="submit" value="Submit" />
</form>
フォームには、テキスト入力、ファイル入力、送信ボタンが含まれます。
JavaScript は次のとおりです。
const form = document.querySelector("#userinfo");
async function sendData() {
  // FormData オブジェクトをフォーム要素に関連付ける
  const formData = new FormData(form);
  try {
    const response = await fetch("https://example.org/post", {
      method: "POST",
      // FormData インスタンスをリクエスト本体として設定
      body: formData,
    });
    console.log(await response.json());
  } catch (e) {
    console.error(e);
  }
}
// フォーム送信を引き継ぐ
form.addEventListener("submit", (event) => {
  event.preventDefault();
  sendData();
});
フォーム要素に送信イベントハンドラーを追加します。最初の呼び出しでは、ブラウザーの組み込みフォーム送信を防ぐために preventDefault() が呼び出され、その後、フォーム要素を取得してそれを FormData コンストラクターに渡す sendData() が呼び出されます。
その後、fetch() を使用して、 HTTP の POST リクエストとして FormData インスタンスを送信します。
関連情報
>学習コース
上級トピック
- JavaScript によるフォームの送信
- カスタムフォームコントロールの作成方法
- 古いブラウザーでの HTML フォーム
- フォームへの高度なスタイル設定