Jak wyzwolić zdarzenie po użyciu event.preventDefault ()

155

Chcę zorganizować wydarzenie, dopóki nie będę gotowy do jego odpalenia np

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

   e.preventDefault(); 

   // do lots of stuff

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

}

Czy istnieje odpowiednik run()funkcji opisanej powyżej?

Mazatek
źródło
Domyślne zachowanie występuje dopiero po powrocie programu obsługi. Nie ma sensu zapobiegać takiemu zachowaniu tylko po to, by pozwolić na to później w twoim treserze.
Frédéric Hamidi
7
@ FrédéricHamidi Niestety, elementy asynchroniczne ($ .ajax, wywołania zwrotne itp.) Pozwolą na domyślne zachowanie.
vzwick

Odpowiedzi:

164

Nie. Gdy wydarzenie zostanie odwołane, zostaje odwołane.

Możesz jednak ponownie uruchomić zdarzenie później, używając flagi, aby określić, czy Twój kod niestandardowy już został uruchomiony, czy nie - na przykład (zignoruj ​​rażące zanieczyszczenie przestrzeni nazw):

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

Bardziej uogólnionym wariantem (z dodatkową korzyścią w postaci uniknięcia zanieczyszczenia globalnej przestrzeni nazw) może być:

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

Stosowanie:

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

Dodatkowa super-minimalistyczna wtyczka jQuery z Promiseobsługą:

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

Stosowanie:

$('.button').onButFirst('click',
    function(){
        console.log('doing lots of work!');
    },
    function(){
        console.log('done lots of work!');
    });
vzwick
źródło
4
.live jest pozbawiony praw. Użyj .on użytego w przykładzie @Cory Danielson poniżej.
nwolybug
Znowu wchodzę do środka .click i w końcu widzę „zbyt dużą rekursję”
Himanshu Pathak,
5
@HimanshuPathak - prawdopodobnie zapomniałeś ustawić lots_of_stuff_already_done = true;flagi - w przeciwnym razie nie ma możliwości, aby funkcja ciągle się powtarzała.
vzwick,
73

Nowsza wersja zaakceptowanej odpowiedzi.

Krótka wersja:

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

});



Dobrym przypadkiem użycia czegoś takiego jak to jest sytuacja, w której możesz mieć starszy kod formularza, który działa, ale poproszono Cię o ulepszenie formularza przez dodanie czegoś takiego jak weryfikacja adresu e-mail przed przesłaniem formularza. Zamiast przekopywać się przez kod pocztowy formularza zaplecza, możesz napisać interfejs API, a następnie zaktualizować kod frontonu, aby najpierw trafił do tego interfejsu API, zanim zezwolisz formularzowi na wykonanie tradycyjnego POST.

Aby to zrobić, możesz zaimplementować kod podobny do tego, który napisałem tutaj:

$('#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
źródło
1
„Zamiast przekopywać się przez kod pocztowy formularza zaplecza”… W rzeczywistości i tak musisz to zrobić, nie możesz polegać wyłącznie na weryfikacji po stronie klienta.
Diego V,
18

Możesz zrobić coś takiego

$(this).unbind('click').click();
Rafael Oliveira
źródło
To naprawdę fajne rozwiązanie - ale wydaje się, że nie działa w IE10 / 11; (
JonB,
47
Dlaczego ocenzurowałeś słowo „ból”?
Sean Kendle
Uruchomiłeś kliknięcie, ale czy możesz kliknąć ponownie?
Tom Anderson
16

Zastąp właściwość w isDefaultPreventednastępujący sposób:

$('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, jest to najbardziej kompletny sposób ponownego wywołania zdarzenia z dokładnie tymi samymi danymi.

Tomislav Simić
źródło
ejest niezdefiniowana. powinno być evt.preventDefault(). Próbowałem edytować, ale moje zmiany muszą mieć więcej niż 6 znaków i właśnie dodałem 2 :(
kevnk
3
@kevnk, zazwyczaj dołączam krótki opis zmiany w formie komentarza liniowego. Powinno to zwiększyć liczbę przesyłanych znaków.
powtórzyć
Nie wiem, dlaczego ta odpowiedź nie została bardziej przegłosowana, to jest naprawdę przydatne. Działa z propagacją zatrzymuje się również z event.isPropagationStopped = function(){ return false; };. Dodałem również właściwość niestandardową do zdarzenia, aby móc wykryć w module obsługi, czy sprawdzenie, które uniemożliwiło wykonanie, zostało wykonane, więc nie jest wykonywane ponownie. Wspaniały!
Kaddath
Użyłem do Bootstrap 4 Tabs, działało idealnie. Wielkie dzięki. $ ('# v-pills-tab a'). on ('click', function (e) {e.preventDefault (); setTimeout (function () {e.isDefaultPrevented = function () {return false;} $ ( '# v-pills-home-tab'). on ('shown.bs.tab', function () {$ ('. mainDashboard'). show (); $ ('# changePlans'). hide (); });}, 1000); $ (this) .tab ('show');});
Surya R Praveen
8

Jest możliwość korzystania currentTargetz event. Przykład pokazuje, jak postępować z przesłaniem formularza. Podobnie możesz uzyskać funkcję z onclickatrybutu itp.

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

  // code

  event.currentTarget.submit();
});
Salvis Blūzma
źródło
przesyłanie nie jest prawidłową funkcją
Devaffair
jeśli wywołasz submit()ten sam element, czy nie wrócisz do swojego kodu `` $ ('form'). on ('submit')) i powtarzasz go w kółko?
Fanie Void
7

Po prostu nie wykonuj tego e.preventDefault();lub wykonuj to warunkowo.

Z pewnością nie możesz zmienić, kiedy zachodzi pierwotna akcja zdarzenia.

Jeśli chcesz "odtworzyć" oryginalne zdarzenie interfejsu użytkownika jakiś czas później (powiedzmy, w wywołaniu zwrotnym dla żądania AJAX), będziesz musiał po prostu sfałszować to w inny sposób (jak w odpowiedzi vzwicka) ... kwestionować użyteczność takiego podejścia.

Lekkość wyścigów na orbicie
źródło
3

tak długo, jak „dużo rzeczy” nie robi czegoś asynchronicznego, jest to absolutnie niepotrzebne - zdarzenie wywoła sekwencję każdego handlera na swojej drodze, więc jeśli istnieje zdarzenie onklick w elemencie nadrzędnym, zostanie uruchomione po onclik- zdarzenie dziecka zostało całkowicie przetworzone. javascript nie wykonuje tutaj pewnego rodzaju „wielowątkowości”, która powoduje konieczność „zatrzymania” przetwarzania zdarzenia. wniosek: „wstrzymywanie” zdarzenia tylko po to, aby je wznowić w tym samym module obsługi, nie ma sensu.

jeśli „dużo rzeczy” jest czymś asynchronicznym, to również nie ma sensu, ponieważ uniemożliwia to asynchronicznym rzeczom zrobienie tego, co powinny (rzeczy asynchroniczne) i sprawia, że ​​zachowują się tak, jakby wszystko było w kolejności (gdzie wracamy do mojego pierwszego akapitu )

oezi
źródło
Proces w środku jest asynchroniczny, chcę odpalić wynik w wywołaniu zwrotnym
Ajax
1
jeśli musisz czekać na żądanie ajax, zrób je zsynchronizowanym (dla jquery jest async-fag: api.jquery.com/jQuery.ajax ) ... ale tworzenie synchronicznego żądania ajax jest złym pomysłem w prawie każdym przypadku, więc lepiej byłoby znaleźć inne rozwiązanie.
oezi
3

Podejście, które stosuję, jest następujące:

$('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
źródło
2

Zaakceptowane rozwiązanie nie zadziała, jeśli pracujesz z tagiem kotwicy. W takim przypadku po zadzwonieniu nie będzie można ponownie kliknąć łącza e.preventDefault(). Dzieje się tak, ponieważ zdarzenie kliknięcia generowane przez jQuery jest po prostu warstwą nad natywnymi zdarzeniami przeglądarki. Zatem wywołanie zdarzenia „kliknięcia” w tagu kotwicy nie spowoduje podążania za linkiem. Zamiast tego możesz użyć biblioteki, takiej jak jquery-simulate , która pozwoli ci uruchomić natywne zdarzenia przeglądarki.

Więcej szczegółów na ten temat można znaleźć w tym linku

Miguel Carvajal
źródło
1

Innym rozwiązaniem jest użycie window.setTimeout w detektorze zdarzeń i wykonanie kodu po zakończeniu procesu zdarzenia. Coś jak...

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

Używam 0 na okres, ponieważ nie obchodzi mnie czekanie.

Αλέκος
źródło
1

Wiem, że ten temat jest stary, ale myślę, że mogę wnieść swój wkład. Możesz wyzwolić domyślne zachowanie zdarzenia dla określonego elementu w dowolnym momencie w swojej funkcji obsługi, jeśli znasz już to zachowanie. Na przykład, gdy wyzwalasz zdarzenie kliknięcia przycisku resetowania, w rzeczywistości wywołujesz funkcję resetowania w najbliższym formularzu jako zachowanie domyślne. W swojej funkcji obsługi, po użyciu funkcji PreventDefault, możesz przywołać domyślne zachowanie, wywołując funkcję resetowania w najbliższej formie w dowolnym miejscu kodu modułu obsługi.

Patrik
źródło
0

Jeśli ten przykład może pomóc, dodaje „niestandardowe potwierdzenie popin” do niektórych linków (zachowuję kod „$ .ui.Modal.confirm”, to tylko przykład wywołania zwrotnego, które wykonuje oryginalną akcję):

//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
źródło