How to preventing multiple clicks?

There is always couple of operations in any web application which takes bit more time to process and show result then usual operations. These operations like submitting an order, or entering & validating any credit information, or processing for a telecom service etc. usually involves some extensive back-end processes, heavy database operations, or third parties web service involvements do takes time which leads to the user on other end waiting for the result. And that sometime leads to multiple clicks by the user and hence you often find multiple entry in the database for the same request.

Most widely accepted solution to preventing multiple clicks is disabling submit button using Javascript on first click so that user cannot click second time. Some application developers handle this issue through redirecting user to the another page to display some kind of processing status which prevents user for multiple clicks. This works fines in most cases but still majority of developers wants their users to stay on the same page while application is processing their request. Another alternative is to disable the action button once user click on it first time. Problem with disabling server side button using Javascript in ASP.NET based application is that you will then not able raise any server side event of that button. ASP.NET will not trigger onclick event of the button where you have place your process code, if you disable that button on server side.

So here is a small work around to disable server side button and you still able to raise its server side events.

To understand this implement, first drop a button in the page with name say "buttonOrder" for button and "labelStatus" for label. We want user to unable to click buttonOrder second time. To do that, we will disable the button on client side before sending user request to the server. To implement client side script to button event, add an attribute to the button for onclick client side event in the Page_Load event of the page.

Here is the code snippet which explains the implementation:

private void Page_Load(object sender, System.EventArgs e)
{
    buttonOrder.Attributes.Add("onclick", "buttonOrder.disabled=true;" +         this.Page.GetPostBackEventReference(buttonOrder).ToString());
}

private void buttonOrder_Click(object sender, System.EventArgs e)
{
    System.Threading.Thread.Sleep(10000);
}

To emulate the long processing of requested operation, I am using Sleep method of the thread to wait for 10 seconds.

Now let's test this implementation.

When user click on the buttonOrder, button will first disable itself through Javascript to prevent further clicks. With disabled button, ASP.NET will no longer recognize server event of the button. We just to post back the page somehow to tell ASP.NET that onClick event is raised by buttonOrder. To do that, we have a method GetPostBackEventReference in Page class, which allow to add post back event to any button even if it is any HTML control. When you call GetPostBackEventReference method, ASP.NET will place __doPostBack call with the client side event of the control. This basically emulates the post back event of the control. On debugging this implementation, you will see that even though button in question is disabled but it still able to raise server side event.

 

Here is the complete rendered HTML output of the page:

<body MS_POSITIONING="GridLayout">
    <form name="Form1" method="post" action="WebForm2.aspx" id="Form1">
        <input type="hidden" name="__EVENTTARGET" value="" />
        <input type="hidden" name="__EVENTARGUMENT" value="" />
        <input type="hidden" name="__VIEWSTATE" value="dDwtNjI3MTU0N/ng==" />

        <script language="javascript">
            <!--
            function __doPostBack(eventTarget, eventArgument) {
            var theform;
            if (window.navigator.appName.toLowerCase().indexOf("netscape") > -1) {
                theform = document.forms["Form1"];
            }
            else {
                theform = document.Form1;
            }
            theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
            theform.__EVENTARGUMENT.value = eventArgument;
            theform.submit();
            }
            // -->
        </script>
        <span id="labelProcessStatus">Label</span>
        <input type="submit" name="buttonOrder" value="Place Order" id="buttonOrder" onclick="buttonOrder.disabled=true;__doPostBack('buttonOrder','')" /><br />
    </form>
</body>

I hope you will find this hack useful.