🥷

form内でbuttonのonclickで処理しつつ、バリデーションも効かせる方法

に公開3

formタグの中に複数のボタンがある場合

JavaScriptでAPIを叩き、HTTP通信をしたい場面では、次のような構造になることがあります:

<form>
  <input type="text" required />
  <button>下書き保存</button>
  <button>公開</button>
</form>

このとき、実現したいことは主に以下の3点です:

  1. フォームのデフォルト挙動(ページ更新)を防ぐ(event.preventDefault()
  2. ブラウザのバリデーション(例:required属性)を有効にしたい
  3. 「下書き保存」「公開」などボタンごとに異なる処理をしたい

formのonsubmitに関数を仕込む方法

<form onsubmit="submit(event)">
  <input type="text" required />
  <button name="save">下書き保存</button>
  <button name="publish">公開</button>
</form>

この方法では、submit 関数の中で event.preventDefault() を呼び、ページ更新を防ぐことができます。さらに、ブラウザのバリデーション(required)も機能します。

ただし、どちらのボタンが押されたかを判別するには、以下のように event.submitter.name を使います:

const submit = (event) => {
  event.preventDefault();

  const type = event.submitter.name;
  if (type === 'save') {
    // 保存処理
  } else if (type === 'publish') {
    // 公開処理
  }
};

この方法のメリット:

  • Enter キーでの送信も対応できる
  • バリデーションも自動で効く

reportValidityを使う方法(onclickで処理を分ける)

もう1つの方法は、button ごとに onclick 関数を指定するパターンです:

<form onsubmit="event.preventDefault()" id="form">
  <input type="text" required />
  <button type="button" onclick="save()">下書き保存</button>
  <button type="button" onclick="publish()">公開</button>
</form>

このとき、各 buttononclick 関数内でフォームバリデーションを手動で実行します:

const form = document.getElementById("form");

let save = () => {
  if (!form.reportValidity()) return;
  console.log("保存処理");
};

let publish = () => {
  if (!form.reportValidity()) return;
  console.log("公開処理");
};

この方法では、バリデーションが通らない場合にブラウザが警告を表示してくれます。

注意:type="button" にすることで submit イベントは発生しません。そのため、Enter キーでの送信も防ぎたい場合は、onsubmit="event.preventDefault()" をフォームに記述しておくと安心です。


まとめ

目的 方法
ボタンごとに処理を分けたい onclick を使う
ブラウザのバリデーションを使いたい form.reportValidity() を呼ぶ
Enterキー送信を防ぎたい onsubmit="event.preventDefault()" をフォームに追加
どのボタンが押されたかを判別したい event.submitter.name を使う(submitイベント経由)

シンプルなフォームでも、実はこうした細かい制御が重要になります。現場でよくある悩みなので、ぜひこの知識を活かしてみてください!

Discussion

junerjuner

どのボタンが押されたかを判別したい

submitter で直接要素比較でいいのでは……?( name の必要はあるのでしょうか……?(究極 dataset の値を見たりでもいいかもしれない。場合によっては .matches(selector) でも良さそうですね。

juthjuth

ありがとうございます!
おっしゃる通り、event.submitter で直接要素を見て、dataset や .matches() などで判別するのもすごくアリですね。

プロジェクトやチームによっては dataset の方が柔軟に運用しやすい場面もあると思うので、ぜひ取り入れてみます🙏

junerjuner

.classList.contains() とかでもいいですね。(色々ある