177 votes

Empêcher la double soumission des formulaires en jQuery

J'ai un formulaire qui prend un peu de temps à être traité par le serveur. Je dois m'assurer que l'utilisateur attend et ne tente pas de soumettre à nouveau le formulaire en cliquant à nouveau sur le bouton. J'ai essayé d'utiliser le code jQuery suivant :

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

Lorsque j'essaie de le faire dans Firefox, tout est désactivé mais le formulaire n'est pas soumis avec les données POST qu'il est censé inclure. Je ne peux pas utiliser jQuery pour soumettre le formulaire parce que j'ai besoin que le bouton soit soumis avec le formulaire, car il y a plusieurs boutons de soumission et je détermine lequel a été utilisé en fonction de la valeur qui est incluse dans le POST. J'ai besoin que le formulaire soit soumis comme il l'est habituellement et je dois tout désactiver juste après.

Merci !

0 votes

Merci à tous pour votre aide !

0 votes

327voto

Nathan Long Points 30303

Mise à jour en 2018 : Je viens de recevoir des points pour cette vieille réponse, et je voulais juste ajouter que la meilleur La solution serait de rendre l'opération idempotente afin que les soumissions en double soient inoffensives.

Par exemple, si le formulaire crée une commande, mettez un ID unique dans le formulaire. La première fois que le serveur voit une demande de création de commande avec cet ID, il doit la créer et répondre "succès". Les soumissions ultérieures devraient également répondre "succès" (au cas où le client n'aurait pas reçu la première réponse) mais ne devraient rien changer.

Les doublons doivent être détectés par un contrôle d'unicité dans la base de données pour éviter les situations de concurrence.


Je pense que votre problème est cette ligne :

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

Vous désactivez TOUTES les entrées, y compris, je suppose, celles dont les données sont censées être soumises par le formulaire.

Pour désactiver uniquement le(s) bouton(s) d'envoi, il faut pourrait faites-le :

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

Cependant, je ne pense pas qu'IE soumettra le formulaire si même ces boutons sont désactivés. Je suggère une approche différente.

Un plugin jQuery pour le résoudre

Nous venons de résoudre ce problème avec le code suivant. L'astuce ici est d'utiliser la fonction jQuery data() pour marquer le formulaire comme déjà soumis ou non. De cette façon, nous n'avons pas à nous occuper des boutons d'envoi, ce qui fait flipper 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;
};

Utilisez-le comme ça :

$('form').preventDoubleSubmission();

S'il y a des formulaires AJAX qui devrait doivent être autorisés à se soumettre plusieurs fois par chargement de page, vous pouvez leur donner une classe l'indiquant, puis les exclure de votre sélecteur comme ceci :

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

2 votes

Juste une indication - ne serait-il pas préférable de mettre en cache le fichier $(this) à un var that = $(this) ?

5 votes

@Stuart.Sklinar - bonne idée. J'ai utilisé $form forme " parce que c'est plus descriptif, et l'initiale $ car selon ma convention, cela signifie que c'est un objet jQuery.

0 votes

Juste un petit problème - le retour $form est en dehors de la portée, et devrait être à l'intérieur du bind.

47voto

Kern3l Points 1977

L'approche temporelle est erronée - comment savoir combien de temps l'action prendra sur le navigateur du client ?

Comment s'y prendre

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

Lorsque le formulaire est soumis, tous les boutons d'envoi sont désactivés.

N'oubliez pas que dans Firefox, lorsque vous désactivez un bouton, cet état est mémorisé lorsque vous revenez en arrière dans l'historique. Pour éviter cela, vous devez activer les boutons au chargement de la page, par exemple.

2 votes

+1 à cause de l'astuce de firefox, mais y a-t-il une autre option que d'activer les boutons au chargement de la page ?

1 votes

C'est la façon la plus propre de procéder. J'essayais de désactiver le bouton d'envoi avec un gestionnaire d'événement de clic mais mon approche causait des problèmes avec chrome. Cette solution fonctionne à merveille.

3 votes

Faites très attention avec cette approche, si vous avez une valeur sur le bouton d'envoi et que vous en avez besoin, le formulaire ne la soumettra pas.

20voto

PTK Points 31

Je pense que la réponse de Nathan Long est la bonne. Pour ma part, j'utilise la validation côté client, et j'ai donc simplement ajouté une condition pour que le formulaire soit valide.

EDIT : Si cette option n'est pas ajoutée, l'utilisateur ne sera jamais en mesure de soumettre le formulaire si la validation côté client rencontre une erreur.

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

9voto

Robin Daugherty Points 1111

event.timeStamp ne fonctionne pas dans Firefox. Renvoyer false n'est pas standard, vous devriez appeler event.preventDefault() . Et pendant que nous y sommes, toujours utiliser des accolades avec une construction de contrôle .

Pour résumer toutes les réponses précédentes, voici un plugin qui fait le travail et fonctionne sur plusieurs navigateurs.

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

Pour répondre à Kern3l, la méthode de temporisation fonctionne pour moi simplement parce que nous essayons d'empêcher un double-clic sur le bouton de soumission. Si le temps de réponse à une soumission est très long, je recommande de remplacer le bouton ou le formulaire d'envoi par une roue.

Le fait de bloquer complètement les soumissions ultérieures du formulaire, comme le font la plupart des exemples ci-dessus, a un effet secondaire néfaste : en cas de panne de réseau, si l'utilisateur veut essayer de soumettre à nouveau le formulaire, il ne pourra pas le faire et perdra les modifications qu'il a apportées. Cela mettrait définitivement un utilisateur en colère.

2 votes

Vous avez raison. Vous pouvez faire l'inverse - désactiver instantanément puis, après un long délai, réactiver. Cela empêche les doubles clics, permet de soumettre à nouveau, mais permet toujours aux utilisateurs retardataires de soumettre à nouveau des données invalides.

4voto

karim79 Points 178055

...mais le formulaire n'est pas soumis avec aucune des données POST qu'il est supposé inclure.

Correct. Les noms/valeurs des éléments de formulaire désactivés ne seront pas envoyés au serveur. Vous devez les définir comme en lecture seule éléments.

En outre, les ancres ne peuvent pas être désactivées de cette manière. Vous devrez soit supprimer leurs HREF (non recommandé), soit empêcher leur comportement par défaut (meilleure solution), par exemple :

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

0 votes

@Adam, oui, tous les gestionnaires de clics attachés se déclencheront. Je pensais que vous vouliez juste les empêcher d'être modifiés ?

0 votes

@karim79 Je ne me soucie pas de savoir si les gestionnaires de clics sont déclenchés, je veux juste m'assurer que le formulaire n'est pas resoumis par l'utilisateur qui clique sur un autre bouton d'envoi.

0 votes

@Adam - non, cela n'arrivera pas. Dans mon exemple, j'ai ajouté $('input[type=submit]').attr("disabled", "disabled"); mais ma façon préférée est de mettre onclick="this.disabled = 'disabled'" dans le onclick de l'objet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X