Button Submit Once!

Double-submits are as old as forms on the web: the user clicks “Submit”, nothing appears to happen fast enough, they click again, and now you’ve charged the card twice. The classic fix — disable the button on submit — is still right, but the 2008 implementations had landmines worth knowing about.

The old way, and why it broke

The era-typical script sniffed document.all to detect Internet Explorer, then looped through form elements disabling every submit and reset button from an onSubmit attribute. document.all is long dead, and the inline-handler style didn’t compose. Worse, it had a subtle bug baked in: a disabled submit button is not included in the form submission. Some browsers would therefore submit without the button’s name/value pair — breaking any server code that checked if (isset($_POST['save'])).

The modern version

Attach a submit listener and disable after the browser has captured the submission:

document.querySelector('form').addEventListener('submit', (e) => {
  const button = e.target.querySelector('button[type="submit"]');
  if (button.disabled) {
    e.preventDefault(); // guard against re-entry
    return;
  }
  button.disabled = true;
  button.textContent = 'Submitting…';
});

If you ever trigger submission programmatically, use form.requestSubmit() rather than form.submit() — the latter skips HTML validation and submit event handlers entirely, which defeats the guard.

Modern CSS makes the disabled state self-explanatory:

button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}

The real fix is server-side

Client-side disabling is a courtesy for slow connections, not a guarantee — a reload, a back-button resubmit, or a retrying HTTP client bypasses it completely. For anything with side effects, make the operation idempotent on the server:

  • Issue a one-time token when rendering the form; reject any submission whose token was already consumed.
  • Or include a client-generated request ID and deduplicate on it.
  • Post-Redirect-Get (PRG) pattern after successful submission kills the back-button resubmit case.

Do the button-disable for UX, the idempotency for correctness.

Leave a Reply

Your email address will not be published. Required fields are marked *