jQuery / Method / .submit() — フォーム送信イベント

フォームの提出は$(form).on("submit", handler)で受けます。ブラウザー検証を経て提出を求めるならform.requestSubmit()です。旧jQuery .submit()短縮形、jQueryのイベント発火、標準form.submit()では検証とイベントの経路が異なります。

接続と送信要求を分ける

jQuery .submit()短縮形は3.3で非推奨です。関数ありは接続、引数なしは発火です。試した完全版3.7.1・4.0.0でも呼べますが、新規コードでは意味の明確なAPIを使います。非推奨と削除を混同しません。

submitイベントはボタンでなくフォームに発生します。そこで処理すればクリック・Enter・コード要求をまとめられます。クリックだけの検査は別経路を見落とします。

方式 検証とイベント 目的
.on("submit", handler) 接続のみで送信しない 提出処理
送信ボタン / requestSubmit() 制約を検査し有効ならsubmit 通常の要求
jQuery .trigger("submit") ハンドラー実行は同じ制約検証経路ではない 意図したイベント発火
標準form.submit() 制約とsubmitイベントを迂回 特別な迂回処理

経路を比べる完全な例

UTF-8のsubmit-demo.htmlとして保存し開きます。ネットから完全版jQuery4.0.0を読み、サーバーやビルド不要です。全提出をpreventDefaultで止め、結果だけ表示し外部送信・保存はしません。Windows Edgeで4.0.0・3.7.1の経路と提出ボタン情報を検証しました。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Inspect form submit events</title>
  <script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
  <style>
    body { font: 16px/1.6 sans-serif; max-width: 720px; margin: 20px; }
    label { display: block; margin-top: 12px; }
    input, select, button { font: inherit; max-width: 100%; margin: 5px 0; }
    pre { white-space: pre-wrap; overflow-wrap: anywhere; }
  </style>
</head>
<body>
  <form id="chooser">
    <label for="person">Name</label>
    <input id="person" name="person" required>
    <label for="fruit">Fruit</label>
    <select id="fruit" name="fruit" required>
      <option value="">Choose a fruit</option>
      <option value="apple">Apple</option><option value="banana">Banana</option>
    </select>
    <button id="inside" type="submit" name="intent" value="inside">Inspect inside</button>
  </form>
  <button id="outside" type="submit" form="chooser" name="intent" value="outside">Inspect outside</button>
  <button id="request" type="button">requestSubmit(inside)</button>
  <button id="trigger" type="button">jQuery trigger submit</button>
  <button id="restore" type="button">Reset</button>
  <label><input id="automatic" type="checkbox">Request submission when fruit changes</label>
  <pre id="result" aria-live="polite">events=0
No submission inspected.</pre>
  <script>
    jQuery(function ($) {
      const form = document.querySelector("#chooser");
      let events = 0;
      $(form).on("submit", function (event) {
        event.preventDefault(); events++;
        if (!form.reportValidity()) {
          $("#result").text("events=" + events + "\nInvalid data; not inspected."); return;
        }
        const submitter = event.originalEvent ? event.originalEvent.submitter : null;
        const data = submitter ? new FormData(form, submitter) : new FormData(form);
        $("#result").text("events=" + events + "\nsubmitter=" + (submitter ? submitter.id : "(none)") +
          "\nvalues=" + JSON.stringify(Object.fromEntries(data)));
      });
      $("#request").on("click", function () { form.requestSubmit(document.querySelector("#inside")); });
      $("#trigger").on("click", function () { $(form).trigger("submit"); });
      $("#fruit").on("change", function () {
        if (document.querySelector("#automatic").checked) form.requestSubmit();
      });
      $("#restore").on("click", function () {
        form.reset(); document.querySelector("#automatic").checked = false; events = 0;
        $("#result").text("events=0\nNo submission inspected.");
      });
    });
  </script>
</body>
</html>

初期表示です。eventsはハンドラーに入った回数で保存成功数ではありません。

events=0
No submission inspected.
  1. 空のままinside・outside・requestSubmit(inside)を試すとrequiredが先に止めevents=0です。
  2. jQuery trigger submitはevents=1へ進みますがreportValidityでInvalid data; not inspected.です。
  3. Reset後JBとAppleを入力しInspect insideでevents=1、submitter=insideと各値が出ます。
  4. Inspect outsideはsubmitter=outside・intent=outsideです。form="chooser"で外側のボタンを関連付けます。
  5. requestSubmit(inside)はそのボタンを指定します。NameでEnterもこの例では最初のinsideを使います。
  6. 有効値でjQuery発火はsubmitter=(none)、ボタンintentなしです。
  7. 自動要求を有効にしてBananaへ変えると、引数なしrequestSubmit()でsubmitter=(none)です。
  8. Resetは値・自動要求チェック・回数を戻します。

event.originalEvent.submitterは標準SubmitEventのボタンです。合成jQueryイベントにoriginalEventがない場合へ分岐します。FormData(form, submitter)はそのボタンのname/valueも含めます。name重複時のObject.fromEntriesは最後だけ残すため、複数値収集へそのまま転用しません。

外部操作・自動要求・中止

実button type="submit"とform属性なら外側でも標準のキーボード・提出機能を使えます。外にあるだけでrequiredを迂回するわけではなく、使うAPIが重要です。

requestSubmit()は制約検証を経て有効ならイベントを出します。指定要素はそのフォームに関連する提出ボタンでなければなりません。例は指定ありとなしの両方です。

select変更で即提出すると方向キーの選択中に移動することがあります。例は明示選択で有効化し移動も止めています。フィルター即適用か確定ボタンかを先に決めます。.val()設定だけでchangeは出ません。

preventDefaultは標準送信を止めます。非同期処理を待つならイベント中に先に止め、await後から既に始まった提出を取り消そうとしません。この例に非同期通信はなく、実装時は処理状態・重複・復旧・サーバー検証を別に設計します。

旧短縮形と標準メソッド

初期化後に次を実行します。文書に付けないフォームが標準動作を止めるのでサーバー要求はありません。

const $probe = $("<form>");
let calls = 0;
$probe.submit(function (event) {
  event.preventDefault();
  calls++;
});
$probe.submit();
console.log("legacy calls=" + calls);

Console結果です。旧コードでは関数引数の有無を区別します。

legacy calls=1

.trigger("submit")はハンドラーを実行し、中止しなければ標準メソッドへ続く場合があります。ボタンクリックと制約検証の完全再現ではありません。例は合成イベントでもreportValidityを確認します。

HTMLFormElement.submit()は検証とsubmitハンドラーを迂回します。別試験で空requiredをiframeへ提出し要求をローカルで捕捉、空データとイベント0を確認しました。実受信サーバーの保存試験ではありません。

form.submit is not a functionならnameやidがsubmitの要素によるメソッド隠蔽も確認します。resetとの衝突も避け、jQuery集合とDOMフォームを区別します。requestSubmitはDOMメソッドなので[0]などで実要素を取得します。

実送信には適切なaction・method・受信処理が必要で、例の常時中止と表示を変更します。Valid表示は保存完了ではなく、ブラウザーrequiredはサーバー検証を代替しません。