简体   繁体   English

如何在不丢失任何数据的情况下防止双重表单提交?

[英]How to prevent double form submit without losing any data?

I have a HTML form with two buttons我有一个带有两个按钮的 HTML 表单

<form action="...">
    ...
    <input type="submit" name="button1" value="Localized button 1">
    <input type="submit" name="button2" value="Otro botón">
<form>

and following JavaScript code并遵循 JavaScript 代码

// prevent double submit and display a nice animation while submitting
$(document).on("submit", "form", function(event)
{
    $(this).find("input[type=submit], button").each(function()
    {
        var $button = $(this);
        $button.attr("disabled", "disabled");
        $button.addClass("submitting");
        setTimeout(function()
        {
            $button.removeAttr("disabled");
            $button.removeClass("submitting");
        }, 10000); // remove disabled status after timeout (ms)
    });
});

(plus some CSS transitions and animations to make .submitting look nice) (加上一些 CSS 过渡和动画,使.submitting看起来不错)

The above code tries to prevent double form submission because some people seem to misunderstand the web and keep double clicking normal form submit buttons.上面的代码试图防止重复提交表单,因为有些人似乎误解了网络并一直双击正常的表单提交按钮。 Some other people have problems with touchscreens due shaking hands or poorly working broken mouse buttons so this about usability, too.其他一些人由于握手或损坏的鼠标按钮工作不佳而遇到触摸屏问题,因此这也与可用性有关。

The problem is that Google Chrome runs the event handler on("submit") before collecting the data to be sent with the form submission.问题是谷歌浏览器收集要与表单提交一起发送的数据之前on("submit")运行事件处理程序。 As a result, the HTML5 rule of not sending the input value if its control is disabled gets in the way and prevents any submit buttons from being included in the request that the server can see.因此,HTML5 规则(如果其控件被禁用则不发送输入值)会妨碍并阻止任何提交按钮包含在服务器可以看到的请求中。 (The server is looking for parameter button1 or button2 but ignores actual value because it's a localized string due historical HTML form rules.) (服务器正在寻找参数button1button2但忽略实际值,因为由于历史 HTML 表单规则,它是一个本地化的字符串。)

How to disable submit buttons on browsers that honor HTML5 rules and still get info about the actual button that was pressed?如何在遵守 HTML5 规则的浏览器上禁用提交按钮并仍然获取有关按下的实际按钮的信息? I very much want to have a solution that has semantically correct HTML5 source.我非常想要一个具有语义正确的 HTML5 源代码的解决方案。 Is there a way to run code after the form data has been collected and about to be transferred to the server?有没有办法在收集表单数据并准备传输到服务器运行代码?

Somewhat related non-duplicates (please, do not flag this as duplicate of any of these):有点相关的非重复项(请不要将此标记为其中任何一项的重复项):

Additional requirements:其他要求:

  • Allow re-submitting the form after a delay (10 sec in the example above) to allow user to workaround eg interrupted network connection or to do intentional double submission.允许在延迟(上例中为 10 秒)后重新提交表单,以允许用户解决例如中断的网络连接或有意重复提交。
  • No racy solutions (eg running the above code with a short timeout after the actual on("submit") event hoping that the browser has collected the data but the user isn't fast enough to double click the button.没有活泼的解决方案(例如,在实际的on("submit")事件之后以短超时运行上述代码,希望浏览器已收集数据,但用户的速度不够快,无法双击按钮。
  • The form should still work with JavaScript disabled - not having double form submit prevention in this case is okay.该表单应该仍然可以在禁用 JavaScript 的情况下工作 - 在这种情况下没有双重表单提交是可以的。
  • No hacks such as adding a hidden form input containing the missing parameter into the DOM.没有黑客,例如将包含缺少参数的隐藏表单输入添加到 DOM 中。

How about hijacking the click using onClick() and submitting the form before disabling the buttons?如何使用onClick()劫持点击并在禁用按钮之前提交表单?

[untested] [未经测试]

$('input[type=submit]').click(function(e) {
    var $submitButton = $(e.target);

    // prevent automatic form submission
    e.preventDefault();

    // prevent double click without disabling
    if (! $submitButton.hasClass('submitting')) {
        $submitButton.addClass('submitting');

        // submit your form contents
        $('#your-form-id').submit();

        // disable all submit buttons
        $(this).find("input[type=submit], button").each(function()
        {
            var $button = $(this);
            $button.attr("disabled", "disabled");
            $button.addClass("submitting");
            setTimeout(function()
            {
                $button.removeAttr("disabled");
                $button.removeClass("submitting");
            }, 10000); // remove disabled status after timeout (ms)
        });
    }
});

Here's an implementation that seems to be robust enough for production use:这是一个对于生产使用来说似乎足够健壮的实现:

    // prevent double submit and display a nice animation while submitting
    $(document).on("submit", "form", function(event)
    {
        $(this).find("input[type=submit], button").each(function()
        {
            var $button = $(this);

            if ($button.attr("disabled"))
                return; // this button is already disabled, do not touch it

            setTimeout(function()
            {
                $button.attr("disabled", "disabled");
                $button.addClass("submitting");
                setTimeout(function()
                {
                    $button.removeAttr("disabled");
                    $button.removeClass("submitting");
                }, 10000); // remove disabled status after timeout (ms)
            }, 0); // let the event loop run before disabling the buttons to allow form submission to collect form data (and emit "formdata" event) before disabling any buttons and hope that user is not fast enough to double submit before this happens
        });
    });

The setTimeout(..., 0) is a hack to allow Chrome to run its event loop once to get Chrome to collect the form data before we disable the buttons which would drop the info about the button from the submitted data. setTimeout(..., 0)是一个hack,它允许Chrome 运行一次事件循环,让Chrome 在我们禁用按钮之前收集表单数据,这些按钮会从提交的数据中删除有关按钮的信息。 As far as I know, this is not racy with user input because we can use delay of 0 here and there's no way for user to enter any input with zero delay.据我所知,这对用户输入并不敏感,因为我们可以在这里使用0延迟,并且用户无法以零延迟输入任何输入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM