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

How to prevent double form submit without losing any data?

我有一个带有两个按钮的 HTML 表单

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

和以下 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)
    });
});

(加上一些 CSS 过渡和动画使 .submitting 看起来不错)

上面的代码试图防止双重表单提交,因为有些人似乎误解了网络并不断双击正常的表单提交按钮。其他一些人由于手抖或鼠标按钮损坏而无法使用触摸屏,所以这也与可用性有关。

问题是 Google Chrome 运行 事件处理程序 on("submit") 收集要发送的数据之前与表单提交。因此,HTML5 规则 不发送 如果其控件被禁用,则输入值会妨碍并阻止任何提交按钮包含在请求中服务器可以看到。 (服务器正在寻找参数 button1button2 但忽略了实际值,因为它是根据历史 HTML 表单规则的本地化字符串。)

如何在遵守 HTML5 条规则的浏览器上禁用提交按钮并仍然获取有关按下的实际按钮的信息? 我非常想要一个具有语义正确 HTML5 来源的解决方案。 运行 在 表单数据已收集并即将传输到服务器后,是否可以运行 编码

有些相关的非重复项(请不要将其标记为任何这些项的重复项):

  • Disabling double submit prevent sending submit name
  • Prevent double submits
  • Prevent double submit

附加要求:

  • 允许在延迟(上例中为 10 秒)后重新提交表单,以允许用户解决问题,例如网络连接中断或故意重复提交。
  • 没有活泼的解决方案(例如 运行在实际的 on("submit") 事件发生后,用一个短的超时设置上面的代码,希望浏览器已经收集了数据,但用户的速度不够快,无法加倍单击按钮。
  • 表单在 JavaScript 禁用的情况下仍然可以工作 - 在这种情况下没有双重表单提交预防是可以的。
  • 没有黑客攻击,例如将包含缺失参数的隐藏表单输入添加到 DOM。

如何使用 onClick() 劫持点击并在禁用按钮之前提交表单?

[未测试]

$('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)
        });
    }
});

下面是一个似乎足以用于生产的实现:

    // 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
        });
    });

setTimeout(..., 0) 是一个 hack,允许 Chrome 到 运行 其事件循环一次,以便 Chrome 在我们禁用按钮之前收集表单数据从提交的数据中删除有关按钮的信息。据我所知,这对用户输入来说不是活泼的,因为我们可以在这里使用 0 的延迟,并且用户无法以零延迟输入任何输入。