web-dev-qa-db-fra.com

Comment déclencher un événement après avoir utilisé event.preventDefault ()

Je veux organiser un événement jusqu'à ce que je sois prêt à le déclencher, par exemple

$('.button').live('click', function(e){

   e.preventDefault(); 

   // do lots of stuff

   e.run() //this proceeds with the normal event    

}

Existe-t-il un équivalent de la fonction run() décrite ci-dessus?

132
Ash

Nan. Une fois l'événement annulé, il est annulé.

Vous pouvez cependant déclencher ultérieurement l'événement en utilisant un indicateur pour déterminer si votre code personnalisé a déjà été exécuté ou non - comme celui-ci (veuillez ignorer la pollution flagrante de l'espace de noms):

var lots_of_stuff_already_done = false;

$('.button').on('click', function(e) {
    if (lots_of_stuff_already_done) {
        lots_of_stuff_already_done = false; // reset flag
        return; // let the event bubble away
    }

    e.preventDefault();

    // do lots of stuff

    lots_of_stuff_already_done = true; // set flag
    $(this).trigger('click');
});

Une variante plus généralisée (avec l'avantage supplémentaire d'éviter la pollution de l'espace de noms global) pourrait être:

function onWithPrecondition(callback) {
    var isDone = false;

    return function(e) {
        if (isDone === true)
        {
            isDone = false;
            return;
        }

        e.preventDefault();

        callback.apply(this, arguments);

        isDone = true;
        $(this).trigger(e.type);
    }
}

Usage:

var someThingsThatNeedToBeDoneFirst = function() { /* ... */ } // do whatever you need
$('.button').on('click', onWithPrecondition(someThingsThatNeedToBeDoneFirst));

Bonus super-minimaliste plugin jQuery avec Promise support:

(function( $ ) {
    $.fn.onButFirst = function(eventName,         /* the name of the event to bind to, e.g. 'click' */
                               workToBeDoneFirst, /* callback that must complete before the event is re-fired */
                               workDoneCallback   /* optional callback to execute before the event is left to bubble away */) {
        var isDone = false;

        this.on(eventName, function(e) {
            if (isDone === true) {
                isDone = false;
                workDoneCallback && workDoneCallback.apply(this, arguments);
                return;
            }

            e.preventDefault();

            // capture target to re-fire event at
            var $target = $(this);

            // set up callback for when workToBeDoneFirst has completed
            var successfullyCompleted = function() {
                isDone = true;
                $target.trigger(e.type);
            };

            // execute workToBeDoneFirst callback
            var workResult = workToBeDoneFirst.apply(this, arguments);

            // check if workToBeDoneFirst returned a promise
            if (workResult && $.isFunction(workResult.then))
            {
                workResult.then(successfullyCompleted);
            }
            else
            {
                successfullyCompleted();
            }
        });

        return this;
    };
}(jQuery));

Usage:

$('.button').onButFirst('click',
    function(){
        console.log('doing lots of work!');
    },
    function(){
        console.log('done lots of work!');
    });
141
vzwick

Une version plus récente de la réponse acceptée.

Brève version:

$('#form').on('submit', function(e, options) {
    options = options || {};

    if ( !options.lots_of_stuff_done ) {
        e.preventDefault();
        $.ajax({
            /* do lots of stuff */
        }).then(function() {
            // retrigger the submit event with lots_of_stuff_done set to true
            $(e.currentTarget).trigger('submit', { 'lots_of_stuff_done': true });
        });
    } else {
        /* allow default behavior to happen */
    }

});



Un bon cas d'utilisation pour quelque chose comme ceci est où vous pouvez avoir un code de formulaire existant qui fonctionne, mais on vous a demandé d'améliorer le formulaire en ajoutant quelque chose comme une validation d'adresse e-mail avant de soumettre le formulaire. Au lieu de chercher dans le code postal du formulaire principal, vous pouvez écrire une API, puis mettre à jour votre code frontal pour qu'il frappe d'abord cette API avant de permettre au formulaire de faire son POST traditionnel.

Pour ce faire, vous pouvez implémenter un code similaire à ce que j'ai écrit ici:

$('#signup_form').on('submit', function(e, options) {
    options = options || {};

    if ( !options.email_check_complete ) {

        e.preventDefault(); // Prevent form from submitting.
        $.ajax({
            url: '/api/check_email'
            type: 'get',
            contentType: 'application/json',
            data: { 
                'email_address': $('email').val() 
            }
        })
        .then(function() {
            // e.type === 'submit', if you want this to be more dynamic
            $(e.currentTarget).trigger(e.type, { 'email_check_complete': true });
        })
        .fail(function() {
            alert('Email address is not valid. Please fix and try again.');
        })

    } else {

        /**
             Do traditional <form> post.
             This code will be hit on the second pass through this handler because
             the 'email_check_complete' option was passed in with the event.
         */

        $('#notifications').html('Saving your personal settings...').fadeIn();

    }

});
68
Cory Danielson

Vous pouvez faire quelque chose comme

$(this).unbind('click').click();
18
Rafael Oliveira

Remplacez la propriété isDefaultPrevented comme ceci:

$('a').click(function(evt){
  evt.preventDefault();

  // in async handler (ajax/timer) do these actions:
  setTimeout(function(){
    // override prevented flag to prevent jquery from discarding event
    evt.isDefaultPrevented = function(){ return false; }
    // retrigger with the exactly same event data
    $(this).trigger(evt);
  }, 1000);
}

IMHO, c'est la manière la plus complète de redéclencher l'événement avec exactement les mêmes données.

12
Tomislav Simić

Il est possible d'utiliser currentTarget de la event. L'exemple montre comment procéder avec la soumission de formulaire. De même, vous pouvez obtenir la fonction de l'attribut onclick etc.

$('form').on('submit', function(event) {
  event.preventDefault();

  // code

  event.currentTarget.submit();
});
8
Salvis Blūzma

Juste n'effectuez pas e.preventDefault();, ou exécutez-le sous condition.

Vous ne pouvez certainement pas modifier when l'action d'événement d'origine se produit.

Si vous voulez "recréer" l'événement d'interface utilisateur d'origine un certain temps plus tard (par exemple, dans le rappel d'une demande AJAX), il vous suffira alors de le simuler d'une autre manière (comme dans la réponse de vzwick). ..mais je doute que la facilité d'utilisation d'une telle approche.

6

Une réponse plus récente utilise habilement jQuery.one()

$('form').one('submit', function(e) {
    e.preventDefault();
    // do your things ...

    // and when you done:
    $(this).submit();
});

https://stackoverflow.com/a/41440902/510905

4
Oranges13

tant que "beaucoup de choses" ne fait pas quelque chose d'asynchrone, c'est absolument inutile - l'événement appellera chaque gestionnaire en ordre, donc s'il y a un événement onklick sur un élément parent, il sera déclenché après le onclik- événement de l'enfant a complètement traité. javascript ne fait pas ici une sorte de "multithreading" qui rend "arrêter" le traitement d'événement nécessaire. conclusion: "mettre en pause" un événement juste pour le résumer dans le même gestionnaire n'a aucun sens.

si "Beaucoup de choses" est Quelque chose d'asynchrone, cela n'a pas de sens car cela empêche les choses asynchrones de faire ce qu'elles devraient faire (choses asynchones) et de les rendre bahave comme si tout était en séquence (où nous revenons à mon premier paragraphe)

3
oezi

L'approche que j'utilise est la suivante:

$('a').on('click', function(event){
    if (yourCondition === true) { //Put here the condition you want
        event.preventDefault(); // Here triggering stops
        // Here you can put code relevant when event stops;
        return;
    }
    // Here your event works as expected and continue triggering
    // Here you can put code you want before triggering
});
3
Hokusai

La solution acceptée ne fonctionnera pas si vous utilisez une balise d'ancrage. Dans ce cas, vous ne pourrez plus cliquer sur le lien après avoir appelé e.preventDefault(). C’est parce que l’événement click généré par jQuery est une couche superposée aux événements du navigateur natif. Donc, déclencher un événement 'clic' sur une balise d'ancrage ne suivra pas le lien. À la place, vous pouvez utiliser une bibliothèque telle que jquery-simulate qui vous permettra de lancer des événements de navigateur natifs.

Plus de détails à ce sujet peuvent être trouvés dans ceci lien

2
Miguel Carvajal

Je sais que ce sujet est ancien mais je pense pouvoir y contribuer. Vous pouvez déclencher le comportement par défaut d'un événement sur un élément spécifique à tout moment dans votre fonction de gestionnaire si vous connaissez déjà ce comportement. Par exemple, lorsque vous déclenchez l'événement click sur le bouton de réinitialisation, vous appelez la fonction de réinitialisation du formulaire le plus proche en tant que comportement par défaut. Dans votre fonction de gestionnaire, après avoir utilisé la fonction preventDefault, vous pouvez rappeler le comportement par défaut en appelant la fonction de réinitialisation sur la fiche la plus proche, n'importe où dans le code de votre gestionnaire.

1
Patrik

Une autre solution consiste à utiliser window.setTimeout dans l'écouteur d'événements et à exécuter le code une fois le processus de l'événement terminé. Quelque chose comme...

window.setTimeout(function() {
  // do your thing
}, 0);

J'utilise 0 pour la période écoulée depuis que je ne me soucie pas d'attendre.

1
Αλέκος

Si cet exemple peut aider, ajoute un "message de confirmation personnalisé" sur certains liens (je conserve le code de "$ .ui.Modal.confirm", il ne s'agit que d'un exemple pour le rappel qui exécute l'action d'origine):

//Register "custom confirm popin" on click on specific links
$(document).on(
    "click", 
    "A.confirm", 
    function(event){
        //prevent default click action
        event.preventDefault();
        //show "custom confirm popin"
        $.ui.Modal.confirm(
            //popin text
            "Do you confirm ?", 
            //action on click 'ok'
            function() {
                //Unregister handler (prevent loop)
                $(document).off("click", "A.confirm");
                //Do default click action
                $(event.target)[0].click();
            }
        );
    }
);
0
Ronan Kerdudou