I'm using buttons to submit data via Ajax. I'm using jQuery to disable and enable the buttons. This is to prevent "button-mashing," where a user can fire multiple requests either maliciously or unwittingly.
Is there an "element-agnostic" way to prevent this behavior in jQuery? For example, say I wanted to use anchors instead of buttons to submit the data. A button I can disable; but as far as I know you can not disable anchors.
Here is how I'm doing this now: (note I've removed some of the unnecessary code to make it shorter)
$('.fav .button').click(function() {
$.ajax({
context: this,
dataType: 'json',
beforeSend: function() {
// Toggle state; disable button to prevent button mashing
$(".fav").toggleClass("fav-state-1");
$(".fav .button").attr("disabled", true);
},
error: function() {
// Rollback state and re-enable button on error
$(".fav").toggleClass("fav-state-1");
$(".fav .button").attr("disabled", false);
},
success: function(response) {
if (response.result == "success") {
$(".fav .button").attr("disabled", false);
}
else {
// Rollback state; re-enable button
$(".fav").toggleClass("fav-state-1");
$(".fav .button").attr("disabled", false);
}
}
});
return false;
});
HTML:
<input class="button" type="button" value="">
I'm using buttons to submit data via Ajax. I'm using jQuery to disable and enable the buttons. This is to prevent "button-mashing," where a user can fire multiple requests either maliciously or unwittingly.
Is there an "element-agnostic" way to prevent this behavior in jQuery? For example, say I wanted to use anchors instead of buttons to submit the data. A button I can disable; but as far as I know you can not disable anchors.
Here is how I'm doing this now: (note I've removed some of the unnecessary code to make it shorter)
$('.fav .button').click(function() {
$.ajax({
context: this,
dataType: 'json',
beforeSend: function() {
// Toggle state; disable button to prevent button mashing
$(".fav").toggleClass("fav-state-1");
$(".fav .button").attr("disabled", true);
},
error: function() {
// Rollback state and re-enable button on error
$(".fav").toggleClass("fav-state-1");
$(".fav .button").attr("disabled", false);
},
success: function(response) {
if (response.result == "success") {
$(".fav .button").attr("disabled", false);
}
else {
// Rollback state; re-enable button
$(".fav").toggleClass("fav-state-1");
$(".fav .button").attr("disabled", false);
}
}
});
return false;
});
HTML:
<input class="button" type="button" value="">
Share
Improve this question
edited Dec 10, 2010 at 15:26
hvgotcodes
120k33 gold badges208 silver badges237 bronze badges
asked Dec 10, 2010 at 15:19
MohamadMohamad
35.4k34 gold badges143 silver badges220 bronze badges
3
- 1 You should consider catching multiple form submits server-side instead of client-side. It's more reliable. – RoToRa Commented Dec 10, 2010 at 15:29
- 2 "This is to prevent "button-mashing," where a user can fire multiple requests either maliciously or unwittingly." It's good to try and prevent people unwittingly sending multiple requests, but you can't stop them doing so maliciously. The server has to be robust against that. – Jason Orendorff Commented Dec 10, 2010 at 15:39
- Thanks guys, I failed to mention that I already have a backend mechanism to handle this. I just did not want the server to be bombarded needlessly in such events. – Mohamad Commented Dec 10, 2010 at 17:30
5 Answers
Reset to default 6Of course, the best way to do this would be to handle it gracefully on the server side.
That said, you could use the data storage methods in jQuery to store a value to indicate it has already been clicked, and use that to determine if the user has already clicked/pressed the button. The values get stored per selector, so you can set it on anything.
$("a").click(function(e) {
e.preventDefault();
if (!$(this).data('isClicked')) {
var link = $(this);
// Your code on successful click
// Set the isClicked value and set a timer to reset in 3s
link.data('isClicked', true);
setTimeout(function() {
link.removeData('isClicked')
}, 3000);
} else {
// Anything you want to say 'Bad user!'
}
});
The benefit is that you're not stopping the user from clicking anything else, as it's a per element solution. In your case, you might want to do the link.removeData
in the success function.
Example of it working: http://jsfiddle/jonathon/ke8Az/ (Note that if you try to click again within the 3s, you get the 'Please wait' but you can still click the rest)
Note: This is a client side solution only. And only if they have JavaScript installed. Unless you handle it on the server side, the user can maliciously send multiple requests. This just helps with the 'unwittingly' part.
Before the ajax call you could unbind the click event on the clicked object, then in the success / error method you could rebind the click event.
That way they can mash the button as much as they want but it wont have the click wired up until the call has finished.
You could do the following:
- setup a variable clickedWithinLastSecond.
- in the method that fires the request, check if it is true, if it is then don't send the request
- If it is not send the request and set the variable to true. Setup a method that fires after a second with setTimeout that changes the variable back to false.
you will have to customize variable name to your needs, and you will need to find a way to keep the variable in the correct scopes, but that is not difficult.
The best and only way to stop the behavior you are describing is on the server side.
You can also use debounce
to register only one click. Both underscore.js and lodash.js provide this handy method.
Here's a fiddle to demo the concept. With lazyClick
, you can double/triple click on the link yet the event handler is fired only once.
var lazyClick = _.debounce(onclickHandler, 500);
function onclickHandler(e){
e.preventDefault();
console.log('clicked');
}
$(function(){
// unment this and you will see double clicks are being registered.
//$('#btn').click(onclickHandler);
// with debounce, only one click will be registered
$('#btn').click(lazyClick);
});