Click-submit and Enter-submit must share one lock
Aliases: click and enter lock · form double submit · shared in-flight
What it is
A form can usually submit from the button click and from Enter in a field. If the two channels bind separate handlers with separate in-flight flags, both fire in the same instant. Sharing one lock means click and Enter both try the same in-process lock first, and the loser is dropped. Disabling the button without blocking the form’s submit event still lets Enter send a second copy.
Why it happens
Browsers default to: Enter in a single-line field fires form submit, which then clicks the submit button. Habits are type-then-Enter, type-then-click, or Enter while the mouse is already clicking—the last is common on a slow response. If the click path locks on the button and the Enter path runs form.onsubmit without reading that lock, two paths are two requests. Keyboard repeat (holding Enter) can also emit a string of keydowns before the lock takes. The lock has to sit in front of every entry, synchronously: “wait for setState to disable” postpones the lock a frame, and Enter is already out. Server de-dupe is a backstop; dual client channels still make double load and a brief double-success.
Studying it
On a form with tunable delay, click the button and press Enter together, or hold Enter. Log request count. Compare: lock the button only, lock the button and preventDefault submit, a synchronous lock on the first line of the entry function.
Independent variables: which layer holds the lock, preventDefault or not, whether IME Enter emits another event. Dependent variables: requests per intent, double success toasts.
Automation that tests click and keydown separately will both pass. The two channels have to be stacked at the same moment.
Where it stops holding
A form with two submits (Save draft / Publish) must not share a page-wide lock that kills the other intent, but every entry of the same intent still shares a lock. In a dialog, Enter clicks the default button—a third channel that must enter the same lock. A div button with no form has no submit event, so the problem shrinks to the control’s own key and click.
Applying it
- Make the request function a single entry. Click, Enter, and submit all call it; the first line takes the lock synchronously.
- preventDefault on form submit so the browser does not run native submit as well.
- Do not rely on a next-frame disabled to stop Enter.
- Verify by holding Enter in a field while clicking submit. The network log for that intent must show one request.
Related
- Within the group: E1.17.2 Touch delay can let two quick taps both count as valid · E1.17.3 After a network failure the button must unlock into a clear error · E1.17.4 Optimistic UI can desynchronize unlock from result feedback
- Adjacent: E1.09 Loading buttons · C6 Keyboard and text input · H1 Forms
- Search terms:
double submit·form submit·Enter key