Andy Smith's Blog

Page.RegisterStartupScript('Andy', 'MetaBuilders_WebControls_GainKnowledge();');

you don't want to disable buttons after they are clicked

No, i'm not trying to use jedi mind tricks on you.

You really don't want to go down the road of disabling buttons after they've been clicked. Here's what happens... A developer sees that some user with zero patience is clicking the “Process Page” button a million times while the server is processing the request. This is causing havoc, because your click handler for the button is running a million times, and now you have quite a few records in your database, or database errors about duplicated records, depending on how you have your db set up.

The knee-jerk reaction is, “ok, set the disabled property to true on the button after the first time they click it.”
This is a bad idea.

A disabled button doesn't have it's Name/Value pair sent in the form post. Without the Name/Value pair, the serverside has no idea that the button was clicked. So your Click event doesn't get run.

“Ok, so don't disable it. set the onclick event to just return false immediately.”

Ah, ok, this is a little better... but there's still a couple subtle problems. The user is given no visual cues that the button is no longer functional. This doesn't seem like a problem, unless the original button click didn't actually work. Replace “zero patience user” with “patient user with a horrible net connection”. Let's say the first click didn't work because the net connection died while he was viewing the page. He clicks the button, the button is now “disabled”, but the form post doesn't reach your server at all. So he reconnects to the net, and clicks the button... and nothing happens. Your average user has no clue why your form doesn't work, and leaves promptly, never to return. D'oh.

“couldn't the hit refresh to regain the button's functionality?”

That makes 3 big assumptions about the user:
1) They know what a “refresh” button is
2) They know where the refresh butto is.
3) The know that clicking it will solve their problem.

Sure, YOU understand what's going on... you developed it. How would you feel if you needed to stop your car, turn it off, turn it back on, and continue driving... when you've hit the turn blinker too many times in a row. It's just a horrible answer.

So anyway...

The real answer to Mr Zero Patience Guy is on the serverside. Make your db design able to handle multiple calls to the “Process Page” button gracefully. Now, I don't know enough about bulletproof db design to give you the best-practice for that... It'd depend on your existing db design and processing model, i'd imagine... but trust me, this is where the answer lives.

UPDATE
Alert reader Nicole commented here that the db isn't the best place either. And I agree. I was getting in a hurry to finish the blog entry, and kinda misspoke. I meant to say that the solution is somewhere on the serverside instead of the clientside, whether it be in the db, business layer, or UI layer of the server code. She pointed at a post she's made before about a “submission tracker” concept that I've seen before. And tho I've never seen any actual code samples for the concept, I do think it's a good plan.

Comments

Nicole Calinoiu said:

The db isn't really a great place to handle this either for at least four reasons.

First, particularly when working with inserts, it means quite a bit of additional db work that really has nothing much to do with the data rules and which could potentially prevent perfectly valid inserts (e.g.: two identical and nearly simultaneous order submissions from two different browser windows within the same session on an e-commerce site).

Second, it makes downstream processes responsible for UI logic, which could cause eventual code maintenance/adaptibility problems if new UIs are added later which require different rules.

Third, it means passing "bad" data deeper than necessary within your application, increasing the potential for performance problems.

Fourth, what if the back-end processes and/or db are not under your control? If, for example, you're simply calling someone else's web service with the data received from the client, you can't implement this approach, and the web service provider is rather unlikely to want to do it for you.

A better (at least wrt convenience and performance) approach is described at http://groups.google.com/groups?hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=OnLpLVV0CHA.1656%40TK2MSFTNGP09. This is reasonably trivial to implement (e.g.: via an ASP.NET custom control) and places responsibility for dealing with potential user interaction problems much closer to their source.
# September 11, 2003 8:52 AM

Oddur Magnusson said:

I'd recomend returning false and changing the text property for the button that indicates the request is beeing processed


[send]
-> click
[sending ...] (return false onclick)

- Oddur
# September 11, 2003 10:44 AM

Carson said:

Another idea is to just throw a layer over the button after it is clicked so that it essentially dissapears to the user.
# September 11, 2003 3:25 PM

Andy Smith said:

both of these "disable the button some other way" solutions suffer from the same fatal flaw as the others. It completely breaks the page if the first post didn't actually get to the server. There's no way to try again.
# September 11, 2003 3:38 PM

TrackBack said:

# September 18, 2003 9:04 PM

TrackBack said:

# September 19, 2003 3:36 AM

Dylan Greene said:

I programmed it on my site to disable the button after the first click but I set a hidden field to the value of the button.
# September 19, 2003 2:48 PM

Carlo Falci said:

There is another issue here: validators are a great problem. If you click and automatically disappears with the button and some validators are on, you have a problem> no submit button avaiable anymore...
# September 26, 2003 3:29 PM

TrackBack said:

# January 7, 2004 9:09 PM

TrackBack said:

# January 7, 2004 9:11 PM

Larryb said:

All you need to do is put a layer on it or whatever and remove the layer on a timer, say after 30 seconds.
# January 23, 2004 8:54 AM

JulianG said:

Hey!
What about disabling the button for a while?

Then you enable it again (if the page is still there).

This way if the post fails, the button will be enabled again in a few seconds.

Of course you have no way to know the post failed or it's still posting, but...
what the hell! Maybe it's a good option.
# March 5, 2004 3:03 PM

TBORE Toronto said:

I would still handle it on the server with a session variable set when data is accepted from the user. A hidden field variable is sent along as last_post_id or something. A quick check will reveal if the new request is identical to the previous one.
# April 5, 2004 5:47 AM

John said:

I found that disabling the submit button works fine in Mozilla, but not in IE. The work-around was to create an onSubmit event in the form that disabled the submit button.
# May 15, 2004 6:13 PM

Rupesh said:

I have also faced the same problem but i used floating transparent division for avoiding this issue. All we have to do one simple thing on client click just show  division with 100% width and 100% height which covers all the page (position of div u can keep as absolute according to u r setting) and if user clicks again event will fire on that div not on button.i did same thing and my problem is solved anyway if any of u r having better solution plz add here

# August 9, 2007 7:52 AM

Frank said:

Just a though. Another way to handle this is to spawn a new thread to handle the functionality that the button is supposed to handle. I know that sounds like a lot of work, but it really isn't.

# October 26, 2007 12:10 PM

Mal said:

As a none wokring sample that I have been working on recently I come up with this

<form method="post" action="http://www.google.co.uk">

<input id="text" type="text" width="15">

<input id="btn1" type="submit" onclick="this.disabled=true">

<input id="btn2" type="button" onclick="this.form.btn1.disabled=false" value="Enable Submit Button">

</form>

You can hide the reset button and change the color of the submit button at the same time as showing an animated image that the process has started.

# December 21, 2007 11:51 AM

ajones said:

Disabling a button and still getting a postback is easy just use the following code and it works great all of the time:

cmdButton.Attributes.Add("onclick", "this.value='Please wait...';this.disabled=true;" & GetPostBackEventReference(Me.cmdButton))

Viola, you have a disabled button with postback and preventing any duplicates without doing any server side crap.  Put this code in the Page_Load event.

# September 3, 2008 3:11 PM

Arun said:

# September 11, 2008 12:08 AM

PH81 said:

maybe use javascript to change the onclick event of button so that it asks on next click user that u have just send request and ist still beeing prosessed

do u want to resend your request.

if the first request (or any) is ok it will set the onclick to original value that button is ready to click without prompt.

# September 15, 2009 9:04 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)