Como disparar um evento depois de usar event.preventDefault ()

156

Quero realizar um evento até estar pronto para dispará-lo, por exemplo

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

   e.preventDefault(); 

   // do lots of stuff

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

}

Existe um equivalente à run()função descrita acima?

Mazatec
fonte
O comportamento padrão ocorre apenas após o retorno do manipulador. Faz pouco sentido impedir esse comportamento apenas para permitir mais tarde em seu manipulador.
Frédéric Hamidi
7
@ FrédéricHamidi Infelizmente, coisas assíncronas ($ .ajax, retornos de chamada etc.) permitem que o comportamento padrão ocorra.
precisa saber é o seguinte

Respostas:

165

Não. Após o cancelamento, o evento é cancelado.

No entanto, você pode reiniciar o evento mais tarde, usando um sinalizador para determinar se seu código personalizado já foi executado ou não - como este (ignore a flagrante poluição do espaço para nome):

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

Uma variante mais generalizada (com o benefício adicional de evitar a poluição global do espaço para nome) poderia ser:

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 de bônus com Promisesuporte:

(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!');
    });
vzwick
fonte
4
.live é privado. Use .on usado no exemplo de @Cory Danielson abaixo.
Nwolybug 25/03
Esta é novamente ficar dentro .click e finalmente eu ver "demais recursão"
Himanshu Pathak
5
@HimanshuPathak - você provavelmente esqueceu de definir o lots_of_stuff_already_done = true;sinalizador - caso contrário, não há como a função continuar se repetindo.
vzwick
73

Uma versão mais recente da resposta aceita.

Versão resumida:

$('#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 */
    }

});



Um bom caso de uso para algo assim é onde você pode ter algum código de formulário herdado que funcione, mas foi solicitado que você aprimore o formulário adicionando algo como validação de endereço de e-mail antes de enviar o formulário. Em vez de pesquisar no código postal do formulário de back-end, você pode escrever uma API e, em seguida, atualizar seu código de front-end para acessá-la primeiro antes de permitir que o formulário faça o POST tradicional.

Para fazer isso, você pode implementar um código semelhante ao que escrevi aqui:

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

    }

});
Cory Danielson
fonte
1
"Em vez de pesquisar no código postal do formulário de back-end" ... Na verdade, você precisa fazer isso de qualquer maneira, não pode confiar apenas na validação do lado do cliente.
Diego V
18

Você pode fazer algo como

$(this).unbind('click').click();
Rafael Oliveira
fonte
Esta é uma solução muito boa - mas não parece funcionar no IE10 / 11; (
JonB
47
Por que você censurou a palavra "dor"?
Sean Kendle
Você acionou o clique, mas pode clicar novamente?
Tom Anderson
16

Substitua a propriedade isDefaultPreventedassim:

$('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, esta é a maneira mais completa de recuperar o evento com exatamente os mesmos dados.

Tomislav Simić
fonte
eestá indefinido. deveria ser evt.preventDefault(). Tentei editar, mas as minhas edições tem que ser> 6 caracteres e eu acabamos de adicionar 2 :(
kevnk
3
@kevnk, normalmente incluo uma breve descrição da edição na forma de um comentário de linha. Isso deve aumentar a contagem de caracteres enviados.
recurs
não sei por que essa resposta não foi mais votada, isso é realmente útil. Trabalhos com propagação também param com event.isPropagationStopped = function(){ return false; };. Também adicionei uma propriedade customizada ao evento para que eu possa detectar no manipulador se a verificação que impediu a ação foi feita, para que não seja feita novamente. Ótimo!
Kaddath 29/04/19
Eu usei o Bootstrap 4 Tabs, funcionou perfeitamente bem. Muito Obrigado. $ ('# v-pills-tab a'). on ('clique', função (e) {e.preventDefault (); setTimeout (função () {e.isDefaultPrevented = function () {return false;} $ ( '# v-pills-home-tab'). on ('mostrado.bs.tab', function () {$ ('. mainDashboard'). show (); $ ('# changePlans'). hide (); });}, 1000); $ (this) .tab ('show');});
Surya R Praveen
8

É possível usar currentTargeto event. O exemplo mostra como proceder com o envio do formulário. Da mesma forma, você pode obter funções do onclickatributo etc.

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

  // code

  event.currentTarget.submit();
});
Salvis Blūzma
fonte
enviar não é uma função válida #
Devaffair
se você chamar submit()o mesmo elemento, não retornará seu código `` $ ('form'). on ('submit') e o refazerá repetidamente?
Fanie Void
7

Apenas não execute e.preventDefault();ou execute condicionalmente.

Você certamente não pode alterar quando a ação original do evento ocorre.

Se você quiser "recriar" o evento original da interface do usuário algum tempo depois (digamos, no retorno de chamada para uma solicitação AJAX), será necessário falsificá-lo de outra maneira (como na resposta do vzwick) ... questionar a usabilidade de tal abordagem.

Raças de leveza em órbita
fonte
6

Uma resposta mais recente usa habilmente jQuery.one()

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

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

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

Laranjas13
fonte
3

desde que "muitas coisas" não estejam fazendo algo assíncrono, isso é absolutamente desnecessário - o evento chamará todos os manipuladores em seu caminho em sequência; portanto, se houver um evento onklick em um elemento pai, ele será acionado após o onclik- evento da criança foi processado completamente. O javascript não faz algum tipo de "multithreading" aqui que torna "interrompido" o processamento de eventos necessário. conclusão: "pausar" um evento apenas para continuar no mesmo manipulador não faz sentido.

se "muita coisa" é algo assíncrono, isso também não faz sentido, pois impede que as coisas assíncronas façam o que devem fazer (coisas assíncronas) e as fazem se comportar como se tudo estivesse em sequência (onde voltamos ao meu primeiro parágrafo )

oezi
fonte
O processo no meio é assíncrona, eu quero acionar o resultado no retorno de chamada ajax ...
Mazatec
1
se você precisar aguardar uma solicitação de ajax, faça com que seja sinônimo (para jquery, existe o async-fag: api.jquery.com/jQuery.ajax ) ... mas fazer solicitação de ajax sinônima é uma má ideia em quase todos os casos, então seria melhor encontrar uma solução diferente.
oezi 30/09/11
3

A abordagem que eu uso é esta:

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

A solução aceita não funcionará se você estiver trabalhando com uma marca de âncora. Nesse caso, você não poderá clicar no link novamente após ligar e.preventDefault(). Isso ocorre porque o evento de clique gerado pelo jQuery é apenas uma camada sobre os eventos do navegador nativo. Portanto, o acionamento de um evento 'clique' em uma tag de âncora não seguirá o link. Em vez disso, você pode usar uma biblioteca como jquery-simulate que permitirá iniciar eventos do navegador nativo.

Mais detalhes sobre isso podem ser encontrados neste link

Miguel Carvajal
fonte
1

Outra solução é usar window.setTimeout no ouvinte de eventos e executar o código após a conclusão do processo do evento. Algo como...

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

Uso 0 para o período, pois não me importo em esperar.

Αλέκος
fonte
1

Sei que esse tópico é antigo, mas acho que posso contribuir. Você pode disparar o comportamento padrão de um evento em um elemento específico a qualquer momento na função do manipulador, se você já conhece esse comportamento. Por exemplo, quando você aciona o evento click no botão de redefinição, na verdade você chama a função de redefinição no formulário mais próximo como o comportamento padrão. Na sua função de manipulador, depois de usar a função preventDefault, você pode recuperar o comportamento padrão chamando a função de redefinição no formulário mais próximo em qualquer lugar no código do manipulador.

Patrik
fonte
0

Se este exemplo puder ajudar, adicione um "popin de confirmação personalizado" em alguns links (eu mantenho o código "$ .ui.Modal.confirm", é apenas um exemplo do retorno de chamada que executa a ação original):

//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();
            }
        );
    }
);
Ronan Kerdudou
fonte