jQueryでのフォームの二重送信を防止する


170

サーバーの処理に少し時間がかかるフォームがあります。ユーザーが待機し、もう一度ボタンをクリックしてフォームを再送信しないようにする必要があります。次の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に含まれている値によってどれが使用されたかを判断するため、フォームと共にボタンを送信する必要があるためです。通常どおりフォームを送信する必要があり、その直後にすべてを無効にする必要があります。

ありがとう!


皆さんの助けに感謝します!
アダム

回答:


315

2018年の更新:私はこの古い答えに対していくつかのポイントを獲得しました、そしてそれを最高に追加したかっただけです、重複した送信が無害になるように操作をべき等解決策であることです。

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

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


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

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

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

単に送信ボタン(s)は無効にするには、あなたは可能性があり、この操作を行います。

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

2
ただ、ポインタ-キャッシュするために、より良いではないでしょう$(this)var that = $(this)
Stuart.Sklinar 2012年

5
@ Stuart.Sklinar-いい考えです。$formしかし、私は「フォーム」を使用しました。これは、より説明的である$ためです。また、私の慣例では、それがjQueryオブジェクトであることを意味するため、先行しています。
ネイサンロング

21
jqueryを使用する場合は、控えめな検証を検証して、フォームが有効かどうかを確認してください。if($(this).valid)){$ form.data( 'submitted'、true);}
cck

1
@cck素晴らしい、私はそれを使いました。私が削除した余分な閉じ括弧がありました:if($(this).valid){$ form.data( 'submitted'、true);
Brian MacKay 2013年

1
@BrianMacKayフォームが無効で送信ボタンが押された場合、このjqueryプラグインは送信された属性をtrueにマークしますが、控えめな検証は検証エラーによる送信を防ぎます。エラーを修正した後、submitted属性がtrueであるため、フォームを送信できません。
cck 2013年

44

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

どうやってするの

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

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

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


2
Firefoxのヒントのために+1ですが、ページの読み込み時にボタンを有効にする以外に他のオプションはありますか?
Raphael Isidro

1
これが最もクリーンな方法です。クリックイベントハンドラーで送信ボタンを無効にしようとしましたが、私のアプローチではChromeで問題が発生していました。このソリューションは美しく機能します。
Anthony

1
この方法には十分注意してください。送信ボタンに値があり、それが必要な場合、フォームは送信しません。
FabienMénager

最新のjQueryドキュメントによると、.attrではなく.propを使用する必要があります。リファレンス:api.jquery.com/prop/#entry-longdesc-1 ".attr()メソッドの代わりに.prop()メソッドを使用して無効化およびチェックを設定する必要があります。"
J Plana

19

ネイサン・ロングの答えは行く方法だと思います。私はクライアント側の検証を使用しているので、フォームが有効であるという条件を追加しました。

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

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

9

event.timeStampFirefoxでは機能しません。falseを返すことは標準ではないので、を呼び出す必要がありますevent.preventDefault()。そして私たちがそれに向かっている間、常に制御構文で中括弧を使用します

これまでのすべての回答をまとめると、次のプラグインは、この機能を実行し、クロスブラウザで機能します。

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つの悪い副作用があります。ネットワーク障害が発生して再送信を試みた場合、再送信を行うことができず、変更内容が失われます。製。これは間違いなくユーザーを怒らせます。


2
あなたが正しい。あなたは逆に行くことができます-即座に無効にしてから、長いタイムアウトの後に再び有効にします。ダブルクリックを防ぎ、再送信を許可しますが、遅れているユーザーに対しては無効な再送信を許可します。
Ctrl-C

8

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

4

...しかし、フォームには、含まれるはずのPOSTデータが送信されていません。

正しい。無効なフォーム要素の名前/値はサ​​ーバーに送信されません。それらを読み取り専用要素として設定する必要があります

また、そのようにアンカーを無効にすることはできません。これらの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>

@Adam、はい、接続されているクリックハンドラーが発生します。変更されないようにしたいだけだと思いましたか?
karim79

@ karim79クリックハンドラーが呼び出されてもかまいません。ユーザーが別の送信ボタンをクリックしてもフォームが再送信されないようにしたいだけです。
アダム

@アダム-いいえ、それは起こりません。私の例では、追加しました$('input[type=submit]').attr("disabled", "disabled");が、私のお気に入りの方法はonclick="this.disabled = 'disabled'"、送信のonclick に入れることです。
karim79

@ karim79試してみましたが、クリックした送信ボタンがPOSTに含まれていません。クリックされたボタンを特定するために、POSTに含める必要があります
Adam

'a'のクリックイベントのビッドを解除したことを知っていますが、$( 'a')。click(function(e){e.preventDefault(); //またはreturn false;});を使用しないでください。?
ファビオ

4

ネイサンロングのアプローチを改善する可能性があります。既に送信されたフォームを検出するためのロジックを次のロジックに置き換えることができます。

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

1
なぜこのアプローチにタイマーを使うのですか?
Bobort 2017

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 内で簡単に展開して、入力や送信ボタンを無効にすることができます。

乾杯


2

私は、この投稿のアイデアを使用して、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

AJAXを使用してフォームを投稿する場合、set 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;
   }
});

3
<code> asnyc:true </ code>を設定することは、AJAXの本質を超えるリクエストが完了するまでブラウザをフリーズさせるため、不適切なアプローチです
Edwin M

2

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

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

2

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

    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>

1

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

クライアント側の検証がない場合は、ここに記載されている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

これで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

私の解決策:

// 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

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

$.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

送信ボタンの変更:

<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

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

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

これは、ユーザーがEnterキーを押したときにフォームの送信を停止しません。
改訂

0

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

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

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


0

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

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

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

-1

私は非常に類似した問題を次の方法で解決しました:

$("#my_form").submit(function(){
    $('input[type=submit]').click(function(event){
        event.preventDefault();
    });
});
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.