🥷
form内でbuttonのonclickで処理しつつ、バリデーションも効かせる方法
formタグの中に複数のボタンがある場合
JavaScriptでAPIを叩き、HTTP通信をしたい場面では、次のような構造になることがあります:
<form>
<input type="text" required />
<button>下書き保存</button>
<button>公開</button>
</form>
このとき、実現したいことは主に以下の3点です:
- フォームのデフォルト挙動(ページ更新)を防ぐ(
event.preventDefault()
) - ブラウザのバリデーション(例:
required
属性)を有効にしたい - 「下書き保存」「公開」などボタンごとに異なる処理をしたい
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>
このとき、各 button
の onclick
関数内でフォームバリデーションを手動で実行します:
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
submitter で直接要素比較でいいのでは……?( name の必要はあるのでしょうか……?(究極 dataset の値を見たりでもいいかもしれない。場合によっては .matches(selector) でも良さそうですね。
ありがとうございます!
おっしゃる通り、event.submitter で直接要素を見て、dataset や .matches() などで判別するのもすごくアリですね。
プロジェクトやチームによっては dataset の方が柔軟に運用しやすい場面もあると思うので、ぜひ取り入れてみます🙏
.classList.contains() とかでもいいですね。(色々ある