web-dev-qa-db-fra.com

Comment désactiver temporairement un gestionnaire de clics dans jQuery?

Dites que j'ai quelque chose comme ce qui suit pour capturer l'événement click d'un bouton:

$("#button_id").click(function() {
  //disable click event
  //do something
  //re-enable click event
}

Comment désactiver temporairement l'événement de clic du bouton jusqu'à la fin du traitement du clic d'origine? En gros, la div disparaît après avoir cliqué sur le bouton, mais si l'utilisateur clique sur le bouton rapidement plusieurs fois, il traite tous ces clics avant que la div ne puisse disparaître. Je veux "rebounce" le bouton afin que seul le premier clic soit enregistré avant la disparition de la div.

61
jack

J'ai remarqué que ce message était vieux mais il apparaît en haut de page sur Google et que ce type de solution n'était jamais proposé, j'ai donc décidé de le poster quand même.

Vous pouvez simplement désactiver les événements de curseur et les réactiver plus tard via css. Il est pris en charge sur tous les principaux navigateurs et peut s'avérer utile dans certaines situations.

$("#button_id").click(function() {

   $("#button_id").css("pointer-events", "none");
   //do something
   $("#button_id").css("pointer-events", "auto");
}
59
Defain

Il s’agit d’une alternative plus idiomatique aux solutions de variables à état artificiel:

$("#button_id").one('click', DoSomething);

function DoSomething() {
  // do something.

  $("#button_id").one('click', DoSomething);
}

One ne sera exécuté qu'une fois (jusqu'à ce qu'il soit de nouveau attaché). Plus d'infos ici: http://docs.jquery.com/Events/one

37
Dave Ward
$("#button_id").click(function() {
  if($(this).data('dont')==1) return;
  $(this).data('dont',1);
  //do something
  $(this).data('dont',0);
}

Rappelez-vous que $ .data () ne fonctionne que pour les éléments avec ID.

10
Thinker

Vous pouvez dissocier votre gestionnaire avec .off , mais il y a une mise en garde; si vous faites cela, empêchez simplement le gestionnaire d'être à nouveau déclenché alors qu'il est déjà en cours d'exécution, vous devez différer la réassociation du gestionnaire.

Par exemple, considérons ce code, qui utilise une veille à chaud de 5 secondes pour simuler une opération synchrone et onéreuse en termes de calcul effectuée depuis le gestionnaire (comme une manipulation lourde dans le DOM):

<button id="foo">Click Me!</div>
<script>
    function waitForFiveSeconds() {
        var startTime = new Date();
        while (new Date() - startTime < 5000) {}
    }
    $('#foo').click(function handler() {
        // BAD CODE, DON'T COPY AND PASTE ME!
        $('#foo').off('click');
        console.log('Hello, World!');
        waitForFiveSeconds();
        $('#foo').click(handler);
    });
</script>

Cela ne marchera pas. Comme vous pouvez le voir si vous l'essayez dans ce JSFiddle , si vous cliquez sur le bouton alors que le gestionnaire est déjà en cours d'exécution, le gestionnaire s'exécutera une seconde fois à la fin de la première exécution. De plus, du moins dans Chrome et Firefox, cela serait vrai même si vous n’utilisiez pas jQuery et n’utilisiez pas addEventListener et removeEventListener pour ajouter et supprimer le gestionnaire. Le navigateur exécute le gestionnaire après le premier clic, le reliure et la réassociation, et alors _ gère le deuxième clic et vérifie s'il existe un gestionnaire de clics à exécuter.

Pour contourner ce problème, vous devez différer la reliure du gestionnaire à l'aide de setTimeout , afin que les clics survenant pendant l'exécution du premier gestionnaire soient traités (avant) _ vous rattachez le gestionnaire.

<button id="foo">Click Me!</div>
<script>
    function waitForFiveSeconds() {
        var startTime = new Date();
        while (new Date() - startTime < 5000) {}
    }
    $('#foo').click(function handler() {
        $('#foo').off('click');
        console.log('Hello, World!');
        waitForFiveSeconds();

        // Defer rebinding the handler, so that any clicks that happened while
        // it was unbound get processed first.
        setTimeout(function () {
            $('#foo').click(handler);
        }, 0);
    });
</script>

Vous pouvez voir ceci en action sur ce JSFiddle modifié .

Naturellement, cela est inutile si ce que vous faites dans votre gestionnaire est déjà asynchrone, car vous cédez déjà le contrôle au navigateur et le laissez vider tous les événements de clic avant de le réassocier. Par exemple, un code comme celui-ci fonctionnera correctement sans un appel setTimeout:

<button id="foo">Save Stuff</div>
<script>
    $('#foo').click(function handler() {
        $('#foo').off('click');
        $.post( "/some_api/save_stuff", function() {
            $('#foo').click(handler);
        });
    });
</script>
6
Mark Amery

Vous pouvez le faire comme les autres personnes avant moi vous ont dit de regarder:

A.) Utilisez .data de l'élément button pour partager une variable d'apparence (ou une simple variable globale)

if ($('#buttonId').data('locked') == 1)
    return
$('#buttonId').data('locked') = 1;
// Do your thing
$('#buttonId').data('locked') = 0;

B.) Désactiver les signaux de la souris

$("#buttonId").css("pointer-events", "none");
// Do your thing
$("#buttonId").css("pointer-events", "auto");

C.) S'il s'agit d'un bouton HTML, vous pouvez le désactiver (entrée [type = submit] ou bouton)

$("#buttonId").attr("disabled", "true");
// Do your thing
$("#buttonId").attr("disabled", "false");

Mais méfiez-vous des autres discussions! J'ai échoué plusieurs fois parce que mon animation (fondu au début ou à la fin) a pris une seconde, etc. fadeIn/fadeOut prend en charge une fonction de rappel en tant que second paramètre . S'il n'y a pas d'autre moyen de le faire, utilisez simplement setTimeout(callback, delay).

Greets, Thomas

4
Chaoste

Si #button_id implique un bouton HTML standard (comme un bouton d'envoi), vous pouvez utiliser l'attribut 'désactivé' pour le rendre inactif pour le navigateur. 

$("#button_id").click(function() {
    $('#button_id').attr('disabled', 'true');

    //do something

     $('#button_id').removeAttr('disabled');
});   

Cependant, vous devrez peut-être faire attention à l'ordre dans lequel ces choses peuvent se produire. Si vous utilisez la commande jquery hide, vous pouvez inclure le "$ ('# button_id"). RemoveAttr (' disabled '); " dans le cadre d’un rappel, afin que cela ne se produise pas tant que la peau n’est pas terminée.

[edit] exemple de fonction utilisant un callback:

$("#button_id").click(function() {
    $('#button_id').attr('disabled', 'true');
    $('#myDiv').hide(function() { $('#button_id').removeAttr('disabled'); });
});   
2
FerrousOxide

J'ai à peine rencontré ce problème en essayant d'afficher une spinner de chargement pendant que j'attendais la fin d'une fonction. Parce que j'ajoutais le spinner au HTML, le spinner était dupliqué à chaque fois que l'on cliquait sur le bouton. Si vous n'êtes pas contre la définition d'une variable à l'échelle globale, cela fonctionnera bien pour moi.

    var hasCardButtonBeenClicked = '';
    $(".js-mela-card-button").on("click", function(){  
        if(!hasCardButtonBeenClicked){
            hasCardButtonBeenClicked = true;
            $(this).append('<i class="fa fa-circle-o-notch fa-spin" style="margin-left: 3px; font-size: 15px;" aria-hidden="true"></i>');
        }    
    });

Notez que tout ce que je fais est de déclarer une variable, et tant que sa valeur est nul, les actions qui suivent le clic se produiront, puis définiront ensuite la valeur des variables sur "true" (il peut s'agir de n'importe quelle valeur, tant qu'elle n'est pas vide). Désactivez ensuite le bouton jusqu'à ce que le navigateur soit actualisé ou que la variable soit définie sur null. .

En regardant en arrière, il aurait probablement été plus logique de définir simplement la valeur "false" pour la variable hasCardButtonBeenClicked, puis d'alterner entre "true" et "false" selon les besoins.

0
Tyler Edwards

il est préférable d'utiliser l'événement en cours et de ne pas enregistrer le gestionnaire dans le gestionnaire global .

var element =  $("#elemid")[0];
var tempHandler = jQuery._data(element)["events"]["click"][0].handler;
$("#elemid").unbind("click");

// do the job that click not suppose to listen;
$("#elemid").bind("click" , tempHandler );

pour tout gestionnaire

var element =  $("#elemid")[0];
var clickHandlerList = jQuery._data(element)["events"]["click"];
var handlerList = [];
for(var i  = 0 ; i <  clickHandlerList .length ; i++) {
    handlerList .Push(clickHandlerList [i].handler);
}
$("#elemid").unbind("click");
// do the job that click not suppose to listen;
for(var i  = 0 ; i <  handlerList.length ; i++) {
    // return back all handler to element.
    $("#elemid").bind("click" , handlerList[i]);
}
0
mehdi

Essayez d'utiliser .one()

var button = $("#button"),
  result = $("#result"),
  buttonHandler = function buttonHandler(e) {
    result.html("processing...");
    $(this).fadeOut(1000, function() {
      // do stuff
      setTimeout(function() {
        // reset `click` event at `button`
        button.fadeIn({
          duration: 500,
          start: function() {
            result.html("done at " + $.now());
          }
        }).one("click", buttonHandler);

      }, 5000)
    })
  };

button.one("click", buttonHandler);
#button {
  width: 50px;
  height: 50px;
  background: olive;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<div id="result"></div>
<div id="button">click</div>

0
guest271314