JavaScript の formdata イベントのまとめ

JavaScript (以下 JS )の formdata イベントについて簡単にまとめました。

formdata イベントとは

formdata イベントは HTML のフォーム( <form> )の送信処理の直前に発火するイベントです。 従来の submit イベントよりも後のタイミングで発火します。 セットされたイベントハンドラは同期的にディスパッチされるため( dispatched synchronously )、サーバーへの通信はイベントハンドラの処理が完了した後に行われます。

Google Chrome にはバージョン 77 ( 2019 年 9 月リリース)で導入されました。 Chrome 以外のブラウザにいつ頃導入されるのかというのは記事執筆時点ではわかっていません。

formdata イベントを含む一連の処理の正確な流れについては次のページが参考になります:

以下使い方について。

formdata イベントの使い方

formdata イベントでは、イベントハンドラに引数として渡される Event オブジェクトに formData というプロパティがセットされています。 formData の中身は、対象のフォームのデータを一式格納した FormData オブジェクトです。

form.addEventListener('formdata', event => {

  // フォームの FormData オブジェクトにアクセスできる
  console.log(event.formData);

  // キーを確認する
  console.log(Array.from(event.formData.keys()));

  // バリューを確認する
  console.log(Array.from(event.formData.values()));

  // キー・バリューのペアを確認する
  console.log(Array.from(event.formData.entries()));
});

この event.formData を使うことで、フォームデータがサーバーに送信される直前のタイミングでデータの追加・変更・削除がかんたんに行えます。

form.addEventListener('formdata', event => {

  // キー・バリューを追加する
  // キーがすでに存在する場合は同じキーで値を追加する
  event.formData.append('key', 'value');

  // 特定のキーの値をセットする
  // キーがすでに存在する場合は上書き、存在しない場合は追加する
  event.formData.set('key', 'value');

  // 特定のキーのエントリーを削除する
  event.formData.delete('key');
});

FormData オブジェクトの詳細のインタフェースについては Mozilla のページにわかりやすく説明されているので興味がある方はそちらのページを見てみてください。

記事作成時点で FormData が持つメソッド一覧は次のとおりとなっています:

FormData.append()
FormData.delete()
FormData.entries()
FormData.get()
FormData.getAll()
FormData.has()
FormData.keys()
FormData.set()
FormData.values()

formdata イベントのメリットは、フォーム送信時の送信データへのアクセスと追加・変更・削除がかんたんにできることです。

(私が理解しているかぎり)従来からある submit イベントを使っても同様のことはできたのですが、その方法が回りくどくあまり直感的ではありませんでした。 具体的には、 submit イベントのイベントハンドラからはフォームの FormData オブジェクトにアクセスすることができないため、データを追加したいときは「フォームに hidden input 要素を追加する」、データを変更したいときは「フォームの elements プロパティの奥の方で変更を加える」、という間接的なアプローチを採る必要がありました。 要は、フォームのデータをちょろっといじりたいだけでも DOM を操作する必要がありました。

submit を使った場合のフォーム送信データの追加や変更のイメージは次のとおりです:

form.addEventListener('submit', event => {

  // hidden input をフォームに追加することでデータを追加する
  let input = document.createElement('input');
  input.setAttribute('type', 'hidden');
  input.setAttribute('name', 'new key');
  input.setAttribute('value', 'new value');
  event.target.appendChild(knput);

  // フォームの elements の中身を変更することでデータを変更する
  for (let element of event.target.elements) {
    if (element.name === 'target') {
      element.value += ' extra suffix';
    }
  }
});

ちなみに、 formdata イベントはキャンセル不可とのことで、イベントハンドラで event.preventDefault() を呼んでもフォームの送信のキャンセルはできません。 バリデーションをかけ条件によってはデータ送信をキャンセルしたいような場合には従来の submit イベントを使うことになります。

以上です。

コメント

個人的な経験では、フォームを使わず JS 側だけで通信を行う完全な Single Page Application ではなくて、ひとつのページの中でフォームと Vue.js 等のフレームワークを両方使うような場合には、この「フォーム送信時にデータを追加・変更する」という処理をしたくなることが結構ある気がします。 記事作成時点では formdata イベントが登場して間もない & Chrome 以外ではまだ使えないため実プロジェクトで使ったことはありませんが、フォームの送信データを DOM を介さず直接変更できるというのはとても便利なので今後 formata イベントの利用頻度が増えてくることと思います。