Come attivare un evento dopo aver utilizzato event.preventDefault ()


156

Voglio organizzare un evento fino a quando non sono pronto a lanciarlo, ad es

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

   e.preventDefault(); 

   // do lots of stuff

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

}

Esiste un equivalente alla run()funzione sopra descritta?


Il comportamento predefinito si verifica solo dopo la restituzione del gestore. Ha poco senso prevenire questo comportamento solo per consentirlo in seguito nel gestore.
Frédéric Hamidi,

7
@ FrédéricHamidi Sfortunatamente, cose asincrone ($ .ajax, callback, ecc.) Permetteranno che si verifichino comportamenti di default.
vzwick,

Risposte:


165

No. Una volta che l'evento è stato cancellato, viene cancellato.

Tuttavia, puoi ri-attivare l'evento in un secondo momento, utilizzando un flag per determinare se il tuo codice personalizzato è già stato eseguito o meno, ad esempio (ignora l'inquinamento palese dello spazio dei nomi):

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

Una variante più generalizzata (con l'ulteriore vantaggio di evitare l'inquinamento dello spazio dei nomi globale) potrebbe essere:

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

Uso:

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

Plugin jQuery super minimalista con Promisesupporto:

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

Uso:

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

4
.live è deprecato. Usa .on utilizzato nell'esempio @Cory Danielson di seguito.
nwolybug,

Questo è di nuovo entrare in .click e finalmente vedo "troppa ricorsione"
Himanshu Pathak

5
@HimanshuPathak - probabilmente hai dimenticato di impostare la lots_of_stuff_already_done = true;bandiera - altrimenti non c'è modo che la funzione continui a ripetersi.
vzwick,

73

Una versione più recente della risposta accettata.

Breve versione:

$('#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 buon caso d'uso per qualcosa di simile è quello in cui potresti avere un codice del modulo legacy che funziona, ma ti è stato chiesto di migliorare il modulo aggiungendo qualcosa come la convalida dell'indirizzo email prima di inviare il modulo. Invece di scavare nel codice postale del modulo back-end, è possibile scrivere un'API e quindi aggiornare il codice front-end per colpire l'API prima di consentire al modulo di eseguire il POST tradizionale.

Per fare ciò, puoi implementare un codice simile a quello che ho scritto qui:

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

    }

});

1
"Invece di scavare nel codice postale del modulo di back-end" ... In effetti devi farlo comunque, non puoi fare affidamento solo sulla convalida lato client.
Diego V,

18

Puoi fare qualcosa del genere

$(this).unbind('click').click();

Questa è davvero una bella soluzione, ma non sembra funzionare su IE10 / 11; (
JonB,

47
Perché hai censurato la parola "dolore"?
Sean Kendle,

Hai attivato il clic ma puoi fare di nuovo clic?
Tom Anderson,

16

Sostituisci la proprietà in isDefaultPreventedquesto modo:

$('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, questo è il modo più completo per riattivare l'evento con gli stessi dati.


enon è definito. dovrebbe essere evt.preventDefault(). Ho provato a modificare, ma le mie modifiche devono essere> 6 caratteri e ho appena aggiunto 2 :(
kevnk

3
@kevnk, in genere includo una breve descrizione della modifica sotto forma di un commento di riga. Ciò dovrebbe aumentare il conteggio dei personaggi inviati.
recluta il

non so perché questa risposta non sia stata più votata, questo è davvero utile. Funziona anche con la propagazione si ferma con event.isPropagationStopped = function(){ return false; };. Ho anche aggiunto una proprietà personalizzata all'evento in modo da poter rilevare nel gestore se il controllo che ha impedito l'azione è stato eseguito, quindi non viene più eseguito. Grande!
Kaddath,

Ho usato per le schede Bootstrap 4, ha funzionato perfettamente. Grazie molto. $ ('# v-pills-tab a'). on ('click', function (e) {e.preventDefault (); setTimeout (function () {e.isDefaultPrevented = function () {return false;} $ ( '# v-pills-home-tab'). on ('shows.bs.tab', function () {$ ('. mainDashboard'). show (); $ ('# changePlans'). hide (); });}, 1000); $ (this) .tab ('show');});
Surya R Praveen,

8

È possibile utilizzare currentTargetil event. L'esempio mostra come procedere con l'invio del modulo. Allo stesso modo potresti ottenere la funzione dall'attributo onclickecc.

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

  // code

  event.currentTarget.submit();
});

presentare non è una funzione valida
Devaffair,

se chiami submit()sullo stesso elemento, non restituirai il tuo codice `` $ ('form'). on ('submit') e lo ripeterai più volte?
Fanie Void

7

Basta non esibirsi e.preventDefault();o eseguirlo in modo condizionale.

Non puoi certo cambiare quando si verifica l'azione dell'evento originale.

Se vuoi "ricreare" l'evento UI originale qualche tempo dopo (diciamo, nel callback per una richiesta AJAX), dovrai solo falsificarlo in un altro modo (come nella risposta di Vzwick) ... anche se avrei mettere in dubbio l'usabilità di un tale approccio.



3

fintanto che "un sacco di cose" non fa qualcosa di asincrono, questo è assolutamente inutile - l'evento chiamerà ogni gestore sulla sua strada in sequenza, quindi se c'è un evento onklick su un elemento genitore questo si attiverà dopo l'onclik- l'evento del bambino è stato elaborato completamente. javascript qui non fa una sorta di "multithreading" che rende necessario "arrestare" l'elaborazione dell'evento. conclusione: "mettere in pausa" un evento solo per riprenderlo nello stesso gestore non ha alcun senso.

se "un sacco di roba" è qualcosa di asincrono, anche questo non ha senso in quanto impedisce alle cose asincrone di fare ciò che dovrebbero fare (cose asincrone) e renderle bahave come tutto è in sequenza (dove torniamo al mio primo paragrafo )


Il processo nel mezzo è asincrono, voglio lanciare il risultato nel callback ajax ...
Mazatec

1
se devi aspettare una richiesta ajax rendila sincrona (per jquery, c'è il async-fag: api.jquery.com/jQuery.ajax ) ... ma fare una richiesta ajax sincrona è una cattiva idea in quasi tutti i casi, quindi sarebbe meglio trovare una soluzione diversa.
oezi,

3

L'approccio che uso è questo:

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

2

La soluzione accettata non funzionerà nel caso in cui si lavori con un tag anchor. In questo caso non sarà possibile fare nuovamente clic sul collegamento dopo aver chiamato e.preventDefault(). Questo perché l'evento click generato da jQuery è semplicemente sovrapposto agli eventi del browser nativo. Quindi l'attivazione di un evento "clic" su un tag anchor non seguirà il collegamento. Invece potresti usare una libreria come jquery-simulate che ti permetterà di lanciare eventi del browser nativi.

Maggiori dettagli su questo possono essere trovati in questo link


1

Un'altra soluzione è utilizzare window.setTimeout nel listener di eventi ed eseguire il codice al termine del processo dell'evento. Qualcosa di simile a...

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

Uso 0 per il periodo poiché non mi interessa attendere.


1

So che questo argomento è vecchio ma penso di poter contribuire. È possibile attivare il comportamento predefinito di un evento su un elemento specifico in qualsiasi momento nella funzione del gestore se si conosce già quel comportamento. Ad esempio, quando si attiva l'evento click sul pulsante di ripristino, in realtà si chiama la funzione di ripristino sul modulo più vicino come comportamento predefinito. Nella funzione del gestore, dopo aver utilizzato la funzione preventDefault, è possibile richiamare il comportamento predefinito chiamando la funzione di ripristino sul modulo più vicino in qualsiasi punto del codice del gestore.


0

Se questo esempio può essere d'aiuto, aggiunge un "popin di conferma personalizzato" su alcuni collegamenti (mantengo il codice di "$ .ui.Modal.confirm", è solo un esempio per il callback che esegue l'azione originale):

//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();
            }
        );
    }
);
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.