Verhindern Sie die doppelte Übermittlung von Formularen in jQuery

170

Ich habe ein Formular, dessen Verarbeitung einige Zeit in Anspruch nimmt. Ich muss sicherstellen, dass der Benutzer wartet und nicht versucht, das Formular erneut zu senden, indem er erneut auf die Schaltfläche klickt. Ich habe versucht, den folgenden jQuery-Code zu verwenden:

<script type="text/javascript">
$(document).ready(function() {
    $("form#my_form").submit(function() {
        $('input').attr('disabled', 'disabled');
        $('a').attr('disabled', 'disabled');
        return true;
    });
});
</script>

Wenn ich dies in Firefox versuche, wird alles deaktiviert, aber das Formular wird nicht mit den POST-Daten gesendet, die es enthalten soll. Ich kann jQuery nicht zum Senden des Formulars verwenden, da die Schaltfläche mit dem Formular gesendet werden muss, da mehrere Senden-Schaltflächen vorhanden sind und ich feststelle, welche von welchem ​​Wert im POST enthalten ist. Ich muss das Formular wie gewohnt einreichen und danach alles deaktivieren.

Vielen Dank!

Adam
quelle
Vielen Dank an alle für all Ihre Hilfe!
Adam

Antworten:

315

Update im Jahr 2018 : Ich habe gerade einige Punkte für diese alte Antwort erhalten und wollte nur hinzufügen, dass die beste Lösung darin besteht, die Operation idempotent zu machen, damit doppelte Einreichungen harmlos sind.

Wenn das Formular beispielsweise eine Bestellung erstellt, geben Sie eine eindeutige ID in das Formular ein. Wenn der Server zum ersten Mal eine Anforderung zur Auftragserstellung mit dieser ID sieht, sollte er diese erstellen und auf "Erfolg" antworten. Nachfolgende Einsendungen sollten ebenfalls mit "Erfolg" antworten (falls der Client nicht die erste Antwort erhalten hat), aber nichts ändern.

Duplikate sollten über eine Eindeutigkeitsprüfung in der Datenbank erkannt werden, um Rennbedingungen zu vermeiden.


Ich denke, dass Ihr Problem diese Zeile ist:

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

Sie deaktivieren ALLE Eingaben, einschließlich derjenigen, deren Daten das Formular senden soll.

Um nur die Senden-Schaltfläche (n) zu deaktivieren, können Sie Folgendes tun:

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

Ich glaube jedoch nicht, dass der IE das Formular senden wird, wenn auch diese Schaltflächen deaktiviert sind. Ich würde einen anderen Ansatz vorschlagen.

Ein jQuery-Plugin, um es zu lösen

Wir haben dieses Problem gerade mit dem folgenden Code gelöst. Der Trick dabei ist, mit jQuery's data()das Formular als bereits gesendet zu markieren oder nicht. Auf diese Weise müssen wir uns nicht mit den Submit-Buttons herumschlagen, was den IE ausflippt.

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

Verwenden Sie es so:

$('form').preventDoubleSubmission();

Wenn es AJAX-Formulare gibt, die mehrmals pro Seitenladevorgang gesendet werden dürfen, können Sie ihnen eine Klasse geben, die dies angibt, und sie dann wie folgt aus Ihrer Auswahl ausschließen:

$('form:not(.js-allow-double-submission)').preventDoubleSubmission();
Nathan Long
quelle
2
Nur ein Zeiger - wäre es nicht besser, das $(this)zu einem zwischenzuspeichern var that = $(this)?
Stuart.Sklinar
5
@ Stuart.Sklinar - gute Idee. Ich habe $formjedoch - 'form' verwendet, weil es aussagekräftiger ist, und das führende, $weil es nach meiner Konvention bedeutet, dass es sich um ein jQuery-Objekt handelt.
Nathan Long
21
Wenn Sie jquery verwenden, überprüfen Sie die unauffällige Validierung besser, um zu überprüfen, ob das Formular gültig ist. if ($ (this) .valid)) {$ form.data ('submit', true);}
cck
1
@cck Super, das habe ich benutzt. Sie hatten einen zusätzlichen schließenden Paren, den ich entfernt habe: if ($ (this) .valid) {$ form.data ('submit', true);
Brian MacKay
1
@BrianMacKay Wenn das Formular nicht gültig ist und die Schaltfläche "Senden" gedrückt wird, markiert dieses Abfrage-Plugin das Attribut "Senden" als wahr, aber die unauffällige Validierung verhindert das Senden aufgrund von Validierungsfehlern. Nach der Korrektur von Fehlern kann das Formular nicht gesendet werden, da das übermittelte Attribut wahr ist!
cck
44

Der Timing-Ansatz ist falsch - woher wissen Sie, wie lange die Aktion im Browser des Clients dauern wird?

Wie es geht

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

Wenn das Formular gesendet wird, werden alle darin enthaltenen Schaltflächen zum Senden deaktiviert.

Denken Sie daran, dass in Firefox beim Deaktivieren einer Schaltfläche dieser Status gespeichert wird, wenn Sie in den Verlauf zurückkehren. Um dies zu verhindern, müssen Sie beispielsweise beim Laden von Seiten Schaltflächen aktivieren.

Strg-C
quelle
2
+1 wegen des Firefox-Tipps, aber gibt es neben dem Aktivieren von Schaltflächen beim Laden von Seiten noch eine andere Option?
Raphael Isidro
1
Dies ist der sauberste Weg. Ich habe versucht, die Senden-Schaltfläche mit einem Click-Event-Handler zu deaktivieren, aber mein Ansatz verursachte Probleme mit Chrome. Diese Lösung funktioniert wunderbar.
Anthony bis
1
Seien Sie bei diesem Ansatz sehr vorsichtig. Wenn Sie einen Wert auf der Schaltfläche "Senden" haben und diesen benötigen, wird er vom Formular nicht gesendet.
Fabien Ménager
Gemäß der neuesten jQuery-Dokumentation sollten Sie .prop anstelle von .attr verwenden. Referenz: api.jquery.com/prop/#entry-longdesc-1 "Die .prop () -Methode sollte verwendet werden, um deaktiviert und aktiviert anstelle der .attr () -Methode festzulegen."
J Plana
19

Ich denke, Nathan Longs Antwort ist der richtige Weg. Für mich verwende ich die clientseitige Validierung, daher habe ich nur eine Bedingung hinzugefügt, dass das Formular gültig ist.

BEARBEITEN : Wenn dies nicht hinzugefügt wird, kann der Benutzer das Formular niemals senden, wenn bei der clientseitigen Validierung ein Fehler auftritt.

        // 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;
        };
PTK
quelle
9

event.timeStampfunktioniert nicht in Firefox. Die Rückgabe von false ist nicht Standard. Sie sollten anrufen event.preventDefault(). Und wenn wir schon dabei sind, verwenden Sie immer geschweifte Klammern mit einem Kontrollkonstrukt .

Um alle vorherigen Antworten zusammenzufassen, hier ist ein Plugin, das die Arbeit erledigt und browserübergreifend funktioniert.

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

Um Kern3l anzusprechen, funktioniert die Timing-Methode für mich einfach, weil wir versuchen, einen Doppelklick auf die Schaltfläche "Senden" zu stoppen. Wenn Sie eine sehr lange Antwortzeit auf eine Einreichung haben, empfehle ich, die Schaltfläche oder das Formular zum Senden durch einen Drehfeld zu ersetzen.

Das vollständige Blockieren nachfolgender Übermittlungen des Formulars, wie es die meisten der oben genannten Beispiele tun, hat einen schlimmen Nebeneffekt: Wenn ein Netzwerkfehler vorliegt und sie erneut versuchen möchten, dies zu tun, können sie dies nicht und verlieren die Änderungen, die sie vornehmen gemacht. Dies würde definitiv einen verärgerten Benutzer machen.

Robin Daugherty
quelle
2
Du hast recht. Sie können auch umgekehrt vorgehen - sofort deaktivieren und nach einiger Zeit wieder aktivieren. Verhindert Doppelklicks, ermöglicht das erneute Senden, lässt jedoch ungültige erneute Übermittlungen für verzögerte Benutzer zu.
Strg-C
8

Bitte überprüfen Sie das jquery-safeform Plugin.

Anwendungsbeispiel:

$('.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;
    }
});
Max Kamenkov
quelle
4

... aber das Formular wird nicht mit den POST-Daten gesendet, die es enthalten soll.

Richtig. Deaktivierte Formularelementnamen / -werte werden nicht an den Server gesendet. Sie sollten sie als schreibgeschützte Elemente festlegen .

Außerdem können Anker nicht so deaktiviert werden. Sie müssen entweder ihre HREFs entfernen (nicht empfohlen) oder ihr Standardverhalten verhindern (besserer Weg), z. B.:

<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>
karim79
quelle
@Adam, ja, alle angehängten Klick-Handler werden ausgelöst. Ich dachte, Sie wollten nur verhindern, dass sie geändert werden?
Karim79
@ karim79 Es ist mir egal, ob die Klick-Handler ausgelöst werden. Ich möchte nur sicherstellen, dass das Formular nicht erneut gesendet wird, wenn der Benutzer auf eine andere Schaltfläche zum Senden klickt.
Adam
@Adam - nein, das wird nicht passieren. Ich habe mein Beispiel hinzugefügt, $('input[type=submit]').attr("disabled", "disabled");aber meine Lieblingsmethode ist es, onclick="this.disabled = 'disabled'"in den Onclick des Submit zu setzen.
Karim79
@ karim79 Ich habe das versucht, aber dann ist der Senden-Button, auf den ich klicke, nicht im POST enthalten. Ich muss es in den POST aufnehmen, um festzustellen, auf welche Schaltfläche geklickt wurde
Adam
Ich weiß, dass Sie gerade das Klickereignis für das 'a' aufgehoben haben, aber warum nicht einfach das $ ('a') verwenden. Click (Funktion (e) {e.preventDefault (); // oder return false;}); ?
Fabio
4

Es besteht die Möglichkeit, den Ansatz von Nathan Long zu verbessern . Sie können die Logik zur Erkennung bereits übermittelter Formulare durch folgende ersetzen:

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
Mikhail Fiadosenka
quelle
1
Warum um alles in der Welt würden Sie einen Timer für diesen Ansatz verwenden?
Bobort
4

Nathans Code aber für jQuery Validate Plugin

Wenn Sie zufällig das jQuery Validate-Plugin verwenden, ist der Submit-Handler bereits implementiert. In diesem Fall gibt es keinen Grund, mehr als einen zu implementieren. Der Code:

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

Sie können es einfach innerhalb des } else {Blocks erweitern, um Eingaben und / oder die Schaltfläche "Senden" zu deaktivieren.

Prost

Alph.Dev
quelle
2

Am Ende habe ich Ideen aus diesem Beitrag verwendet, um eine Lösung zu finden, die der Version von AtZako ziemlich ähnlich ist.

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

Verwenden Sie wie folgt:

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

Ich habe festgestellt, dass die Lösungen, die keine Zeitüberschreitung, sondern nur die deaktivierte Übermittlung oder deaktivierte Formularelemente enthielten, Probleme verursachten, da Sie nach dem Auslösen der Sperre erst wieder senden können, wenn Sie die Seite aktualisieren. Das bereitet mir einige Probleme, wenn ich Ajax mache.

Dies kann wahrscheinlich ein wenig verschönert werden, da es nicht so schick ist.

Jacklin
quelle
2

Wenn Sie AJAX zum Posten eines Formulars verwenden, async: falsesollte set zusätzliche Übermittlungen verhindern, bevor das Formular gelöscht wird:

$("#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;
   }
});
Al Kari
quelle
3
Das Setzen von <code> asnyc: true </ code> ist ein schlechter Ansatz, da der Browser eingefroren wird, bis die Anforderung abgeschlossen ist, was die Essenz von AJAX
Edwin M
2

Die Lösung von Nathan für Bootstrap 3 wurde ein wenig geändert. Dadurch wird ein Ladetext für die Schaltfläche "Senden" festgelegt. Außerdem tritt nach 30 Sekunden eine Zeitüberschreitung auf, und das Formular kann erneut gesendet werden.

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);
};
AlexZ
quelle
2

Verwenden Sie zwei Senden-Schaltflächen.

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

Und jQuery:

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

Verwenden Sie beim Senden einen einfachen Zähler.

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

Und rufen Sie die monitor()Funktion beim Absenden des Formulars auf.

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

Ich hatte ähnliche Probleme und meine Lösung (en) lauten wie folgt.

Wenn Sie keine clientseitige Validierung haben, können Sie einfach die hier dokumentierte Methode jquery one () verwenden.

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

Dies deaktiviert den Handler, nachdem er aufgerufen wurde.

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

Wenn Sie wie ich eine clientseitige Validierung durchführen, ist dies etwas schwieriger. Im obigen Beispiel können Sie nach einer fehlgeschlagenen Validierung nicht erneut senden. Versuchen Sie stattdessen diesen Ansatz

$("#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);
   }
});
Mattbloke
quelle
1

Sie können die zweite Übermittlung dadurch stoppen

$("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
    });
});
Mohammed Zayan
quelle
0

Meine Lösung:

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

quelle
0

In meinem Fall hatte der Onsubmit des Formulars einen Validierungscode, daher erhöhe ich die Antwort von Nathan Long einschließlich eines Onsubmit-Prüfpunkts

$.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;
    };
Bocapio
quelle
0

Schaltfläche "Senden" ändern:

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

Mit normaler Taste:

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

Verwenden Sie dann die Klickfunktion:

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

Und denken Sie daran, die Schaltfläche erneut zu aktivieren, wenn dies erforderlich ist:

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

Ich kann den guten, altmodischen CSS-Trick von Zeigerereignissen nicht glauben: Keiner wurde noch nicht erwähnt. Ich hatte das gleiche Problem beim Hinzufügen eines deaktivierten Attributs, aber dieses wird nicht zurückgesendet. Versuchen Sie Folgendes und ersetzen Sie #SubmitButton durch die ID Ihrer Senden-Schaltfläche.

$(document).on('click', '#SubmitButton', function () {
    $(this).css('pointer-events', 'none');
})
Tom McDonough
quelle
Dies stoppt das Senden von Formularen nicht, wenn der Benutzer die EnterTaste drückt .
reformiert
0

Warum nicht einfach das? Dadurch wird das Formular gesendet, aber auch die Schaltfläche zum Senden deaktiviert.

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

Wenn Sie jQuery Validate verwenden, können Sie diese beiden Zeilen auch unter setzen if ($('#myForm').valid()).

Gen b.
quelle
0

Dieser Code zeigt das Laden auf der Tastenbeschriftung an und setzt die Taste auf

Deaktivieren Sie den Status, aktivieren Sie ihn nach der Verarbeitung erneut und geben Sie den ursprünglichen Schaltflächentext zurück **

$(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
});
Mohamed.Abdo
quelle
-1

Ich habe ein sehr ähnliches Problem gelöst mit:

$("#my_form").submit(function(){
    $('input[type=submit]').click(function(event){
        event.preventDefault();
    });
});
Nolan
quelle