web-dev-qa-db-ja.com

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にどの値が含まれているかを判断します。通常どおりフォームを送信する必要があり、その直後にすべてを無効にする必要があります。

ありがとう!

156
Adam

2018年の更新:この古い回答に対していくつかのポイントを得ただけで、best解決策は、重複した送信が無害になるように操作をべき等にすることです。

たとえば、フォームが注文を作成する場合、フォームに一意のIDを入力します。サーバーは、そのIDを持つ注文作成リクエストを初めて見ると、それを作成して「成功」と応答する必要があります。後続の送信も「成功」と応答する必要があります(クライアントが最初の応答を取得しなかった場合)が、何も変更しないでください。

競合状態を防ぐために、データベースの一意性チェックを介して重複を検出する必要があります。


あなたの問題はこの行だと思います:

$('input').attr('disabled','disabled');

フォームが送信するデータの入力を含むすべての入力を無効にしています。

送信ボタンのみを無効にするには、couldを行います:

$('button[type=submit], input[type=submit]').prop('disabled',true);

ただし、ボタンが無効になっている場合でも、IEがフォームを送信するとは思わない。別のアプローチをお勧めします。

それを解決するjQueryプラグイン

次のコードでこの問題を解決しました。ここでのコツは、jQueryのdata()を使用して、フォームが既に送信済みかどうかをマークすることです。そうすれば、IEアウトを驚かせる送信ボタンをいじる必要はありません。

// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function() {
  $(this).on('submit',function(e){
    var $form = $(this);

    if ($form.data('submitted') === true) {
      // Previously submitted - don't submit again
      e.preventDefault();
    } else {
      // Mark it so that the next submit can be ignored
      $form.data('submitted', true);
    }
  });

  // Keep chainability
  return this;
};

次のように使用します。

$('form').preventDoubleSubmission();

ページのロードごとにを複数回送信できるAJAXフォームがある場合は、それを示すクラスを与えることができます。次のように、セレクタからそれらを除外します。

$('form:not(.js-allow-double-submission)').preventDoubleSubmission();
297
Nathan Long

タイミングのアプローチが間違っています-クライアントのブラウザでアクションがどのくらいかかるかをどのように知るのですか?

どうやってするの

$('form').submit(function(){
  $(this).find(':submit').attr('disabled','disabled');
});

フォームを送信すると、内部のすべての送信ボタンが無効になります。

Firefoxでボタンを無効にすると、履歴に戻るときにこの状態が記憶されます。それを防ぐには、たとえば、ページの読み込み時にボタンを有効にする必要があります。

42
Ctrl-C

ネイサン・ロングの答えは進むべき道だと思います。私にとっては、クライアント側の検証を使用しているため、フォームが有効であるという条件を追加しただけです。

EDIT:これが追加されていない場合、クライアント側の検証でエラーが発生した場合、ユーザーはフォームを送信できません。

        // jQuery plugin to prevent double submission of forms
        jQuery.fn.preventDoubleSubmission = function () {
            $(this).on('submit', function (e) {
                var $form = $(this);

                if ($form.data('submitted') === true) {
                    // Previously submitted - don't submit again
                    alert('Form already submitted. Please wait.');
                    e.preventDefault();
                } else {
                    // Mark it so that the next submit can be ignored
                    // ADDED requirement that form be valid
                    if($form.valid()) {
                        $form.data('submitted', true);
                    }
                }
            });

            // Keep chainability
            return this;
        };
19
PTK

event.timeStampはFirefoxでは機能しません。 falseを返すことは非標準です。event.preventDefault()を呼び出す必要があります。そして、その間、 always制御構造と共にブレースを使用します

これまでの回答をすべてまとめると、仕事を行い、クロスブラウザーで動作するプラグインがあります。

jQuery.fn.preventDoubleSubmission = function() {

    var last_clicked, time_since_clicked;

    jQuery(this).bind('submit', function(event) {

        if(last_clicked) {
            time_since_clicked = jQuery.now() - last_clicked;
        }

        last_clicked = jQuery.now();

        if(time_since_clicked < 2000) {
            // Blocking form submit because it was too soon after the last submit.
            event.preventDefault();
        }

        return true;
    });
};

Kern3lに対処するために、送信ボタンのダブルクリックを停止しようとしているという理由だけで、タイミングメソッドが機能します。送信に対する応答時間が非常に長い場合は、送信ボタンまたはフォームをスピナーに置き換えることをお勧めします。

上記の例のほとんどがそうであるように、フォームの後続の送信を完全にブロックすることには、1つの悪い副作用があります。製。これは間違いなく怒っているユーザーになります。

9
Robin Daugherty

jquery-safeform プラグインをチェックしてください。

使用例:

$('.safeform').safeform({
    timeout: 5000,  // disable next submission for 5 sec
    submit: function() {
        // You can put validation and ajax stuff here...

        // When done no need to wait for timeout, re-enable the form ASAP
        $(this).safeform('complete');
        return false;
    }
});
8
Max Kamenkov

Nathan Longの approach を改善する可能性があります。すでに送信されたフォームを検出するためのロジックを次のフォームに置き換えることができます。

var lastTime = $(this).data("lastSubmitTime");
if (lastTime && typeof lastTime === "object") {
    var now = new Date();
    if ((now - lastTime) > 2000) // 2000ms
        return true;
    else
        return false;
}
$(this).data("lastSubmitTime", new Date());
return true; // or do an ajax call or smth else
4

Nathanのコード(jQuery Validateプラグイン用)

JQuery Validateプラグインを使用する場合は、既に送信ハンドラが実装されています。その場合、複数の実装ハンドラを実装する理由はありません。コード:

jQuery.validator.setDefaults({
  submitHandler: function(form){
    // Prevent double submit
    if($(form).data('submitted')===true){
      // Previously submitted - don't submit again
      return false;
    } else {
      // Mark form as 'submitted' so that the next submit can be ignored
      $(form).data('submitted', true);
      return true;
    }
  }
});

} else {- block内で簡単に展開して、入力を無効にしたり、ボタンを送信したりできます。

乾杯

4
Alph.Dev

...ただし、フォームは、含まれるはずのPOSTデータのいずれも送信されません。

正しい。無効化されたフォーム要素の名前/値はサ​​ーバーに送信されません。これらをreadonly elementsとして設定する必要があります。

また、そのようにアンカーを無効にすることはできません。 HREFを削除する(推奨されません)か、デフォルトの動作を防ぐ(より良い方法)必要があります。例:

<script type="text/javascript">
$(document).ready(function(){
    $("form#my_form").submit(function(){
      $('input').attr('readonly', true);
      $('input[type=submit]').attr("disabled", "disabled");
      $('a').unbind("click").click(function(e) {
          e.preventDefault();
          // or return false;
      });
    });
</script>
4
karim79

私は結局、この投稿のアイデアを使用して、AtZakoのバージョンと非常によく似たソリューションを考え出しました。

 jQuery.fn.preventDoubleSubmission = function() {

    var last_clicked, time_since_clicked;

    $(this).bind('submit', function(event){

    if(last_clicked) 
      time_since_clicked = event.timeStamp - last_clicked;

    last_clicked = event.timeStamp;

    if(time_since_clicked < 2000)
      return false;

    return true;
  });   
};

このような使用:

$('#my-form').preventDoubleSubmission();

ロックアウトがトリガーされると、ページを更新するまで再度送信できないため、何らかの種類のタイムアウトを含まず、送信またはフォーム要素を無効にするだけのソリューションが問題を引き起こすことがわかりました。これは、ajaxの処理を行うときに問題を引き起こします。

これはおそらく、それほど空想ではないので、少し見栄えがよくなります。

2
jacklin

NathanのソリューションをBootstrap 3に少し変更しました。これにより、ロードボタンに送信テキストが設定されます。さらに、30秒後にタイムアウトし、フォームを再送信できます。

jQuery.fn.preventDoubleSubmission = function() {
  $('input[type="submit"]').data('loading-text', 'Loading...');

  $(this).on('submit',function(e){
    var $form = $(this);

    $('input[type="submit"]', $form).button('loading');

    if ($form.data('submitted') === true) {
      // Previously submitted - don't submit again
      e.preventDefault();
    } else {
      // Mark it so that the next submit can be ignored
      $form.data('submitted', true);
      $form.setFormTimeout();
    }
  });

  // Keep chainability
  return this;
};

jQuery.fn.setFormTimeout = function() {
  var $form = $(this);
  setTimeout(function() {
    $('input[type="submit"]', $form).button('reset');
    alert('Form failed to submit within 30 seconds');
  }, 30000);
};
2
AlexZ

AJAXを使用してフォームを投稿する場合、async: falseを設定すると、フォームがクリアされる前に追加の送信ができなくなります。

$("#form").submit(function(){
    var one = $("#one").val();
    var two = $("#two").val();
    $.ajax({
      type: "POST",
      async: false,  // <------ Will complete submit before allowing further action
      url: "process.php",
      data: "one="+one+"&two="+two+"&add=true",
      success: function(result){
        console.log(result);
        // do something with result
      },
      error: function(){alert('Error!')}
    });
    return false;
   }
});
2
Al Kari

ここに私がそれをする方法があります:

$(document).ready(function () {
  $('.class_name').click(function () {
    $(this).parent().append('<img src="data:image/gif;base64,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" />');
    $(this).hide();
  });
});

クレジット: https://github.com/phpawy/jquery-submit-once

1
phpawy

私は同様の問題を抱えており、私の解決策は次のとおりです。

クライアント側の検証がない場合は、ここに記載されているjquery one()メソッドを使用できます。

http://api.jquery.com/one/

これにより、ハンドラーが呼び出された後に無効になります。

$("#mysavebuttonid").on("click", function () {
  $('form').submit();
});

私がやっているようにクライアント側の検証をしている場合は、少しややこしいです。上記の例では、検証に失敗した後に再度送信することはできません。代わりにこのアプローチを試してください

$("#mysavebuttonid").on("click", function (event) {
  $('form').submit();
  if (boolFormPassedClientSideValidation) {
        //form has passed client side validation and is going to be saved
        //now disable this button from future presses
        $(this).off(event);
   }
});
1
mattbloke

2つの送信ボタンを使用します。

<input id="sub" name="sub" type="submit" value="OK, Save">
<input id="sub2" name="sub2" type="submit" value="Hidden Submit" style="display:none">

そしてjQuery:

$("#sub").click(function(){
  $(this).val("Please wait..");
  $(this).attr("disabled","disabled");
  $("#sub2").click();
});
1
izmir_LEE

送信ボタンの変更:

<input id="submitButtonId" type="submit" value="Delete" />

通常のボタンの場合:

<input id="submitButtonId" type="button" value="Delete" />

次に、クリック機能を使用します。

$("#submitButtonId").click(function () {
        $('#submitButtonId').prop('disabled', true);
        $('#myForm').submit();
    });

必要なときにボタンを再度有効にすることを忘れないでください:

$('#submitButtonId').prop('disabled', false);
0
Dani

ポインタイベントの古き良きCSSトリックを信じることはできません。まだ言及されていません。無効な属性を追加することで同じ問題が発生しましたが、これはポストバックしません。以下を試して、#SubmitButtonを送信ボタンのIDに置き換えます。

$(document).on('click', '#SubmitButton', function () {
    $(this).css('pointer-events', 'none');
})
0
Tom McDonough

なぜこれだけではないのですか?これはフォームを送信するだけでなく、送信ボタンを無効にします。

   $('#myForm').on('submit', function(e) {
       var clickedSubmit = $(this).find('input[type=submit]:focus');
       $(clickedSubmit).prop('disabled', true);
   });

また、jQuery Validateを使用している場合、これらの2行をif ($('#myForm').valid())の下に配置できます。

0
gene b.

私の解決策:

// jQuery plugin to prevent double submission of forms
$.fn.preventDoubleSubmission = function () {
    var $form = $(this);

    $form.find('[type="submit"]').click(function () {
        $(this).prop('disabled', true);
        $form.submit();
    });

    // Keep chainability
    return this;
};
0
user1672591

これで2回目の送信を停止できます

$("form").submit(function() {
        // submit more than once return false
        $(this).submit(function() {
            return false;
        });
        // submit once return true
        return true; // or do what you want to do
    });
});
0
Mohammed Zayan

送信時に単純なカウンターを使用します。

    var submitCounter = 0;
    function monitor() {
        submitCounter++;
        if (submitCounter < 2) {
            console.log('Submitted. Attempt: ' + submitCounter);
            return true;
        }
        console.log('Not Submitted. Attempt: ' + submitCounter);
        return false;
    }

そして、フォームを送信するときにmonitor()関数を呼び出します。

    <form action="/someAction.go" onsubmit="return monitor();" method="POST">
        ....
        <input type="submit" value="Save Data">
    </form>
0
Mr. Mak

私の場合、フォームのonsubmitには検証コードが含まれていたため、 Nathan Long onsubmitチェックポイントを含む回答を増やします

$.fn.preventDoubleSubmission = function() {
      $(this).on('submit',function(e){
        var $form = $(this);
        //if the form has something in onsubmit
        var submitCode = $form.attr('onsubmit');
        if(submitCode != undefined && submitCode != ''){
            var submitFunction = new Function (submitCode);
            if(!submitFunction()){
                event.preventDefault();
                return false;
            }                   
        }

        if ($form.data('submitted') === true) {
            /*Previously submitted - don't submit again */
            e.preventDefault();
        } else {
          /*Mark it so that the next submit can be ignored*/
          $form.data('submitted', true);
        }
      });

      /*Keep chainability*/
      return this;
    };
0
bocapio

このコードは、ボタンラベルにロードを表示し、ボタンを

状態を無効にし、処理後、元のボタンテキストを再度有効にして戻します**

$(function () {

    $(".btn-Loading").each(function (idx, Elm) {
        $(Elm).click(function () {
            //do processing
            if ($(".input-validation-error").length > 0)
                return;
            $(this).attr("label", $(this).text()).text("loading ....");
            $(this).delay(1000).animate({ disabled: true }, 1000, function () {
                //original event call
                $.when($(Elm).delay(1000).one("click")).done(function () {
                    $(this).animate({ disabled: false }, 1000, function () {
                        $(this).text($(this).attr("label"));
                    })
                });
                //processing finalized
            });
        });
    });
    // and fire it after definition
}

);

0
Mohamed.Abdo