Disable the submit button after the form is submitted (jQuery and ASP.NET)
Users double-click buttons, especially when a page is slow. On a payment or order form that means two orders. Disabling the button once the form is on its way prevents most duplicates, but on a Web Forms page there are two traps: a disabled submit button is not posted, so its Click event never fires, and validation may have stopped the submit anyway.
The trap
// Looks right, breaks the server event:
$("#<%= btnOrder.ClientID %>").on("click", function () {
$(this).prop("disabled", true);
});
ASP.NET knows which button was clicked because the browser posts the button's name and value. A button disabled before the form is submitted is not included, so btnOrder_Click silently does nothing.
Solution 1: UseSubmitBehavior="false" (Web Forms)
<asp:Button ID="btnOrder" runat="server" Text="Place order" OnClick="btnOrder_Click"
UseSubmitBehavior="false"
OnClientClick="if (typeof Page_ClientValidate === 'function' && !Page_ClientValidate()) return false; this.disabled = true; this.value = 'Sending…';" />
With UseSubmitBehavior="false", ASP.NET renders a plain button that posts through __doPostBack, which sends the button's identity in the __EVENTTARGET field. Disabling the button no longer loses the event. The script first runs client validation (if validators are on the page) so the button is not disabled for a form that will not be sent.
Solution 2: disable on the form's submit event (jQuery)
$(function () {
$("form").on("submit", function () {
var $form = $(this);
// let validation cancel the submit first
if (typeof Page_IsValid !== "undefined" && !Page_IsValid) return;
// defer so the clicked button's name/value is captured before disabling
setTimeout(function () {
$form.find("input[type=submit], button[type=submit]")
.prop("disabled", true)
.addClass("is-busy");
}, 0);
});
});
The form's submission data is constructed when the submit happens, so disabling the buttons in a zero-delay timeout leaves the clicked button's value in the request. This works for plain HTML forms too. If a validator fails, Web Forms' own script cancels the submit before this handler matters.
With an UpdatePanel, the page is not reloaded after an asynchronous postback, so re-enable the buttons in a Sys.WebForms.PageRequestManager endRequest handler.
The server must still check
Script can be off, a request can be replayed, and two tabs can submit the same order. Make the operation idempotent on the server: generate a unique token when the form is shown (in a hidden field), store it with the order, and reject a second order with the same token, for example with a unique index.
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) hfOrderToken.Value = Guid.NewGuid().ToString("N");
}
protected void btnOrder_Click(object sender, EventArgs e)
{
if (!Guid.TryParseExact(hfOrderToken.Value, "N", out Guid token)) return;
bool created = OrderService.TryCreate(token, ReadOrderFromForm()); // unique index on token
lblResult.Text = created ? "Order placed." : "This order was already received.";
}
A related helper is enabling and disabling a TextBox from a button.
Official documentation
- Microsoft Learn: Button.UseSubmitBehavior, Button.OnClientClick
- jQuery API: submit event, .prop()
- MDN: HTMLFormElement: submit event