form.submit() skips the submit event, skips validation and drops the clicked button. form.requestSubmit() acts like a real click: validation runs, your submit handler fires, and requestSubmit(button) sends that button's name/value. Use requestSubmit() unless you deliberately want to bypass everything.Point any HTML form at one endpoint. 500 free submissions — no card, no time limit — and unlimited forms.
Create free accountform.submit() vs form.requestSubmit() at a glance
| Behaviour | form.submit() | form.requestSubmit() |
|---|---|---|
| Fires the submit event | No | Yes |
| Runs constraint validation (required, pattern) | No | Yes, unless novalidate / formnovalidate |
| Sends the submitter button's name/value | No | Only with requestSubmit(button) |
| Honours the button's formaction / formmethod | No | Yes, with requestSubmit(button) |
| Can be cancelled with preventDefault() | No (no event) | Yes |
const form = document.querySelector("#contact");
const saveBtn = form.querySelector('button[name="intent"][value="save"]');
form.submit(); // no event, no validation, no button value
form.requestSubmit(); // validation + submit event, submitter = null
form.requestSubmit(saveBtn); // same, and sends intent=saveThe button passed to requestSubmit() must be a submit button that belongs to that form; anything else throws. If the form has novalidate (or the button has formnovalidate), requestSubmit() skips validation too, exactly like a click would.
The submit event and preventDefault()
The reliable way to send a form with JavaScript is not to call either method from a click handler. Put your code on the form's submit event, cancel the native navigation with event.preventDefault(), and send the data with fetch:
form.addEventListener("submit", async (e) => {
e.preventDefault(); // no page reload
const data = new FormData(form); // every named, enabled field
await fetch(form.action, { method: "POST", body: data });
});The browser only fires submit after validation passes, so required and pattern checks still protect you. The same handler covers mouse clicks, the Enter key and requestSubmit(). How to read and shape the payload is covered in FormData in JavaScript.
Common bugs: why the submit event or validation does not fire
- A control named
submit.<button name="submit">orid="submit"shadows the method, soform.submit()throws "form.submit is not a function". Rename it, or callHTMLFormElement.prototype.submit.call(form). - Calling
submit()inside the submit handler. A common pattern ise.preventDefault(), an async check, thenform.submit(). It works (no loop, becausesubmit()fires no event), but the clicked button's name/value is lost. Switching that line torequestSubmit()fires the handler again and loops forever, so guard it with a flag or send withfetchinstead. - Buttons without
type="button". A<button>inside a form defaults totype="submit". "Show password", "Add row" or modal buttons then submit the form by accident. Give every non-submit buttontype="button". - Enter-key implicit submission. Pressing Enter in a text input clicks the form's first submit button, so
e.submitteris that button. With no submit button, Enter only submits when the form has a single text-like field. To block it, handle thesubmitevent, notkeydownon each field. - Validation silently skipped.
form.submit()and a click handler that callsfetchboth bypass required and pattern. UserequestSubmit()or callform.reportValidity()first.
For the full checklist (missing name attributes, buttons outside the form, wrong method) see why your HTML form is not submitting. If users double-click, add the guards from preventing duplicate form submissions.
e.submitter and formaction
Every submit event is a SubmitEvent with a submitter property: the button that started it, or null. A native submission uses that button's formaction, formmethod and formnovalidate overrides. Once you call preventDefault() and use fetch, you have to apply them yourself:
form.addEventListener("submit", async (e) => {
e.preventDefault();
const btn = e.submitter;
const action = btn?.getAttribute("formaction") || form.action;
const data = new FormData(form, btn); // includes the button's name/value
await fetch(action, { method: "POST", body: data });
});Read the attribute rather than btn.formAction: the property returns the page URL when the attribute is missing. A button placed outside the form with the form attribute is still the submitter, and requestSubmit() accepts it as an argument.
Working example: requestSubmit() plus fetch to splitforms
<form id="contact" action="https://splitforms.com/api/submit" method="POST">
<input type="hidden" name="access_key" value="YOUR_ACCESS_KEY">
<input name="name" required>
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<button type="submit">Send</button>
<p id="status" role="status"></p>
</form>
<script>
const form = document.getElementById("contact");
const status = document.getElementById("status");
form.addEventListener("submit", async (e) => {
e.preventDefault();
status.textContent = "Sending...";
const res = await fetch(form.action, {
method: "POST",
headers: { Accept: "application/json" },
body: new FormData(form, e.submitter),
});
const result = await res.json();
if (res.ok && result.success === true) {
form.reset();
status.textContent = "Thanks! Your message was sent.";
} else {
status.textContent = result.message || "Could not send. Please try again.";
}
});
// Submitting from elsewhere (a keyboard shortcut, a wizard step)?
// form.requestSubmit() runs validation and the handler above.
</script>Without JavaScript the same markup still posts to the endpoint as a normal form. Status codes and error bodies are listed in the response guide. Get a free access key.
FAQ
What is the difference between form.submit() and form.requestSubmit()?
form.submit() sends the form straight away: no submit event fires, no constraint validation runs (required, pattern, type, minlength are skipped) and no submit button's name/value is included. form.requestSubmit() behaves like a user clicking a submit button: it runs validation, fires the submit event (so your preventDefault handler runs) and can include a submitter button.
Why does my submit event not fire when I call form.submit()?
By design. HTMLFormElement.submit() bypasses the submit event and validation entirely, so addEventListener('submit', ...) and onsubmit handlers never run. Call form.requestSubmit() instead and the event fires as if a user clicked submit.
Why do I get 'form.submit is not a function'?
A control inside the form is named or has the id 'submit', for example <button name="submit">. Named controls become properties of the form element, so form.submit now points at the button, not the method. Rename the control (name="send") or call HTMLFormElement.prototype.submit.call(form).
How do I stop a form from reloading the page?
Listen for the submit event, call event.preventDefault(), then send the data yourself with fetch(url, { method: 'POST', body: new FormData(form) }). Because the handler is on the submit event, it covers button clicks, the Enter key and form.requestSubmit() calls.
What is event.submitter?
On a submit event, event.submitter is the button that triggered the submission, or null when there was none (for example requestSubmit() without an argument). Use it to tell which button was pressed, to read its formaction, and pass it to new FormData(form, event.submitter) so the button's name/value is included.
Browser references: MDN: requestSubmit() and MDN: SubmitEvent.submitter.