防止 jQuery 中表单的重复提交
我有一个表单,服务器需要一些时间才能处理。我需要确保用户等待并且不会尝试通过再次单击按钮来重新提交表单。我尝试使用以下 jQuery 代码:
<script type="text/javascript">
$(document).ready(function() {
$("form#my_form").submit(function() {
$('input').attr('disabled', 'disabled');
$('a').attr('disabled', 'disabled');
return true;
});
});
</script>
当我在 Firefox 中尝试此操作时,所有内容都会被禁用,但表单不会随其应包含的任何 POST 数据一起提交。我无法使用 jQuery 提交表单,因为我需要将按钮与表单一起提交,因为有多个提交按钮,并且我确定 POST 中包含哪个值所使用的按钮。我需要像平常一样提交表单,并且我需要在发生这种情况后立即禁用所有内容。
谢谢!
I have a form that takes a little while for the server to process. I need to ensure that the user waits and does not attempt to resubmit the form by clicking the button again. I tried using the following jQuery code:
<script type="text/javascript">
$(document).ready(function() {
$("form#my_form").submit(function() {
$('input').attr('disabled', 'disabled');
$('a').attr('disabled', 'disabled');
return true;
});
});
</script>
When I try this in Firefox everything gets disabled but the form is not submitted with any of the POST data it is supposed to include. I can't use jQuery to submit the form because I need the button to be submitted with the form as there are multiple submit buttons and I determine which was used by which one's value is included in the POST. I need the form to be submitted as it usually is and I need to disable everything right after that happens.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(24)
2018年更新:我刚刚从这个旧答案中得到了一些观点,只是想补充一点,最佳解决方案是使操作幂等,以便重复提交是无害的。
例如,如果表单创建订单,则在表单中放入唯一的 ID。服务器第一次看到具有该 ID 的订单创建请求时,它应该创建它并响应“成功”。后续提交也应该响应“成功”(如果客户端没有得到第一个响应),但不应该改变任何内容。
应通过数据库中的唯一性检查来检测重复项,以防止竞争情况。
我认为您的问题是这一行:
您正在禁用所有输入,我猜包括表单应该提交其数据的输入。
要仅禁用提交按钮,您可以执行以下操作:
但是,即使禁用了这些按钮,我认为 IE 也不会提交表单。我建议采用不同的方法。
一个 jQuery 插件来解决这个问题
我们刚刚用下面的代码解决了这个问题。这里的技巧是使用 jQuery 的
data()
将表单标记为已提交或未提交。这样,我们就不必弄乱提交按钮,这会吓坏 IE。像这样使用它:
如果有 AJAX 表单应该允许每次页面加载多次提交,您可以给它们一个类来指示这一点,然后将它们从选择器中排除,如下所示:
Update in 2018: I just got some points for this old answer, and just wanted to add that the best solution would be to make the operation idempotent so that duplicate submissions are harmless.
Eg, if the form creates an order, put a unique ID in the form. The first time the server sees an order creation request with that id, it should create it and respond "success". Subsequent submissions should also respond "success" (in case the client didn't get the first response) but shouldn't change anything.
Duplicates should be detected via a uniqueness check in the database to prevent race conditions.
I think that your problem is this line:
You're disabling ALL the inputs, including, I'd guess, the ones whose data the form is supposed to submit.
To disable just the submit button(s), you could do this:
However, I don't think IE will submit the form if even those buttons are disabled. I'd suggest a different approach.
A jQuery plugin to solve it
We just solved this problem with the following code. The trick here is using jQuery's
data()
to mark the form as already submitted or not. That way, we don't have to mess with the submit buttons, which freaks IE out.Use it like this:
If there are AJAX forms that should be allowed to submit multiple times per page load, you can give them a class indicating that, then exclude them from your selector like this:
计时方法是错误的 - 您如何知道客户端浏览器上的操作将花费多长时间?
如何操作
当提交表单时,它将禁用其中的所有提交按钮。
请记住,在 Firefox 中,当您禁用按钮时,当您返回历史记录时,系统会记住此状态。例如,为了防止这种情况,您必须在页面加载时启用按钮。
Timing approach is wrong - how do you know how long the action will take on client's browser?
How to do it
When form is submitted it will disable all submit buttons inside.
Remember, in Firefox when you disable a button this state will be remembered when you go back in history. To prevent that you have to enable buttons on page load, for example.
我认为内森·朗的答案是正确的选择。对我来说,我使用客户端验证,所以我只是添加了一个条件,即表单有效。
编辑:如果不添加此项,如果客户端验证遇到错误,用户将永远无法提交表单。
I think Nathan Long's answer is the way to go. For me, I am using client-side validation, so I just added a condition that the form be valid.
EDIT: If this is not added, the user will never be able to submit the form if the client-side validation encounters an error.
event.timeStamp
在 Firefox 中不起作用。返回 false 是非标准的,您应该调用event.preventDefault()
。当我们这样做时,总是将大括号与控制结构一起使用。总结之前的所有答案,这里有一个可以完成这项工作并跨浏览器工作的插件。
为了解决 Kern3l 问题,计时方法对我来说很有效,因为我们试图阻止双击提交按钮。如果您对提交的响应时间很长,我建议您用微调器替换提交按钮或表单。
正如上面大多数示例所做的那样,完全阻止表单的后续提交会产生一个严重的副作用:如果出现网络故障并且他们想要尝试重新提交,他们将无法这样做,并且会丢失他们所做的更改制成。这肯定会让用户愤怒。
event.timeStamp
doesn't work in Firefox. Returning false is non-standard, you should callevent.preventDefault()
. And while we're at it, always use braces with a control construct.To sum up all of the previous answers, here is a plugin that does the job and works cross-browser.
To address Kern3l, the timing method works for me simply because we're trying to stop a double-click of the submit button. If you have a very long response time to a submission, I recommend replacing the submit button or form with a spinner.
Completely blocking subsequent submissions of the form, as most of the above examples do, has one bad side-effect: if there is a network failure and they want to try to resubmit, they would be unable to do so and would lose the changes they made. This would definitely make an angry user.
请查看 jquery-safeform 插件。
使用示例:
Please, check out jquery-safeform plugin.
Usage example:
正确的。禁用的表单元素名称/值将不会发送到服务器。您应该将它们设置为只读元素。
另外,锚点不能像这样被禁用。您需要删除它们的 HREF(不推荐)或阻止它们的默认行为(更好的方法),例如:
Correct. Disabled form element names/values will not be sent to the server. You should set them as readonly elements.
Also, anchors cannot be disabled like that. You will need to either remove their HREFs (not recommended) or prevent their default behaviour (better way), e.g.:
有可能改进 Nathan Long 的方法。
您可以用以下逻辑替换检测已提交表单的逻辑:
There is a possibility to improve Nathan Long's approach.
You can replace the logic for detection of already submitted form with this one:
Nathan 的代码,但适用于 jQuery Validate 插件
如果您碰巧使用 jQuery Validate 插件,它们已经实现了提交处理程序,在这种情况下,没有理由实现多个。代码:
您可以轻松地在
} else {
块中展开它以禁用输入和/或提交按钮。干杯
Nathan's code but for jQuery Validate plugin
If you happen to use jQuery Validate plugin, they already have submit handler implemented, and in that case there is no reason to implement more than one. The code:
You can easily expand it within the
} else {
-block to disable inputs and/or submit button.Cheers
使用两个提交按钮。
和 jQuery:
Use two submit buttons.
And jQuery:
您可以通过此停止第二次提交
You can stop the second submit by this
我最终使用这篇文章中的想法提出了一个与 AtZako 的版本非常相似的解决方案。
像这样使用:
我发现不包括某种超时但只是禁用提交或禁用表单元素的解决方案会导致问题,因为一旦触发锁定,您就无法再次提交,直到刷新页面。这会给我在做 ajax 的时候带来一些问题。
这可能可以稍微美化一下,因为它并不那么花哨。
I ended up using ideas from this post to come up with a solution that is pretty similar to AtZako's version.
Using like this:
I found that the solutions that didn't include some kind of timeout but just disabled submission or disabled form elements caused problems because once the lock-out is triggered you can't submit again until you refresh the page. That causes some problems for me when doing ajax stuff.
This can probably be prettied up a bit as its not that fancy.
如果使用 AJAX 发布表单,设置
async: false
应该可以防止在表单清除之前进行额外的提交:If using AJAX to post a form, set
async: false
should prevent additional submits before the form clears:针对 Bootstrap 3 稍微修改了 Nathan 的解决方案。这将为提交按钮设置加载文本。此外,它会在 30 秒后超时并允许重新提交表单。
Modified Nathan's solution a little for Bootstrap 3. This will set a loading text to the submit button. In addition it will timeout after 30 seconds and allow the form to be resubmitted.
提交时使用简单计数器。
并在提交表单时调用
monitor()
函数。Use simple counter on submit.
And call
monitor()
function on submit the form.我也遇到过类似的问题,我的解决方案如下。
如果您没有任何客户端验证,那么您可以简单地使用此处记录的 jquery one() 方法。
http://api.jquery.com/one/
这会在调用处理程序后禁用该处理程序。
如果您像我一样进行客户端验证,那么它会稍微棘手一些。上面的示例不会让您在验证失败后再次提交。尝试这种方法
I've been having similar issues and my solution(s) are as follows.
If you don't have any client side validation then you can simply use the jquery one() method as documented here.
http://api.jquery.com/one/
This disables the handler after its been invoked.
If you're doing client side validation as I was doing then its slightly more tricky. The above example would not let you submit again after failed validation. Try this approach instead
只要您不导入 jQuery 验证插件,所有这些解决方案都是可以通过的。
例如,如果客户端输入无效输入并提交表单,则无论 jQuery 验证是否检测到无效输入,该按钮都将被禁用。然后,客户在修复输入后将无法重新提交表单。
此问题的解决方法只需要一个 if 语句:
将此代码添加到我的全局导入的 JavaScript 文件中后,我在尝试在我的网站上重复发布表单时失败了。
All of these solutions are passable as long as you're not importing the jQuery validation plugin.
For example, if the client enters invalid input and submits the form, the button will become disabled regardless of whether jQuery validation detects the invalid input. Then the client won't be able to re-submit the form after fixing their input.
The workaround for this issue only took one if statement:
After adding this code to my globally-imported JavaScript file, I was unsuccessful in trying to double-post forms on my website.
我的解决方案:
My solution:
就我而言,表单的 onsubmit 有一些验证代码,因此我增加 Nathan Long 答案,包括 onsubmit 检查点
In my case the form's onsubmit had some validation code, so I increment Nathan Long answer including an onsubmit checkpoint
更改提交按钮:
使用普通按钮:
然后使用单击功能:
并记住在必要时重新启用按钮:
Change submit button:
With normal button:
Then use click function:
And remember re-enable button when is necesary:
我不敢相信指针事件的老式 CSS 技巧:还没有提到过。我通过添加禁用属性遇到了同样的问题,但这不会回发。请尝试以下操作,并将 #SubmitButton 替换为您的提交按钮的 ID。
I can't believe the good old fashioned css trick of pointer-events: none hasn't been mentioned yet. I had the same issue by adding a disabled attribute but this doesn't post back. Try the below and replace #SubmitButton with the ID of your submit button.
为什么不只是这样——这将提交表单,但也会禁用提交按钮,
另外,如果您使用 jQuery Validate,您可以将这两行放在
if ($('#myForm').valid( ))
。Why not just this -- this will submit the form but also disable the submitting button,
Also, if you're using jQuery Validate, you can put these two lines under
if ($('#myForm').valid())
.此代码将在按钮标签上显示加载,并将按钮设置为
禁用状态,然后在处理后重新启用并返回原始按钮文本**
this code will display loading on the button label, and set button to
disable state, then after processing, re-enable and return back the original button text**
我发现的绝对最好的方法是在单击时立即禁用按钮:
并在需要时重新启用它,例如:
The absolute best way I've found is to immediately disable the button when clicked:
And re-enable it when needed, for example:
我使用以下方法解决了一个非常相似的问题:
I solved a very similar issue using: