So lösen Sie ein Ereignis nach Verwendung von event.preventDefault ()

155

Ich möchte eine Veranstaltung abhalten, bis ich bereit bin, sie auszulösen, z

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

   e.preventDefault(); 

   // do lots of stuff

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

}

Gibt es ein Äquivalent zu der run()oben beschriebenen Funktion?

Mazatec
quelle
Das Standardverhalten tritt erst auf, nachdem Ihr Handler zurückgekehrt ist. Es ist wenig sinnvoll, dieses Verhalten zu verhindern, um es später in Ihrem Handler zuzulassen.
Frédéric Hamidi
7
@ FrédéricHamidi Leider lassen asynchrone Inhalte ($ .ajax, Rückrufe usw.) das Standardverhalten zu.
Vzwick

Antworten:

164

Nee. Sobald die Veranstaltung abgesagt wurde, wird sie abgesagt.

Sie können das Ereignis jedoch später erneut auslösen, indem Sie mithilfe eines Flags feststellen, ob Ihr benutzerdefinierter Code bereits ausgeführt wurde oder nicht - wie folgt (bitte ignorieren Sie die offensichtliche Verschmutzung des Namespace):

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

Eine allgemeinere Variante (mit dem zusätzlichen Vorteil, die Verschmutzung durch globale Namespaces zu vermeiden) könnte sein:

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

Verwendung:

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

Bonus super-minimalistisches jQuery-Plugin mit PromiseUnterstützung:

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

Verwendung:

$('.button').onButFirst('click',
    function(){
        console.log('doing lots of work!');
    },
    function(){
        console.log('done lots of work!');
    });
vzwick
quelle
4
.Live ist beraubt. Verwenden Sie .on, das im folgenden Beispiel für @Cory Danielson verwendet wird.
Nwolybug
Dies ist wieder in .click und endlich sehe ich "zu viel Rekursion"
Himanshu Pathak
5
@HimanshuPathak - Sie haben wahrscheinlich vergessen, das lots_of_stuff_already_done = true;Flag zu setzen - andernfalls kann die Funktion nicht weiter rekursiv sein.
Vzwick
73

Eine neuere Version der akzeptierten Antwort.

Kurzfassung:

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

});



Ein guter Anwendungsfall für so etwas ist, wenn Sie möglicherweise über einen alten Formularcode verfügen, der funktioniert. Sie wurden jedoch gebeten, das Formular zu erweitern, indem Sie vor dem Absenden des Formulars eine Bestätigung der E-Mail-Adresse hinzufügen. Anstatt die Postleitzahl des Back-End-Formulars zu durchsuchen, können Sie eine API schreiben und dann Ihren Front-End-Code so aktualisieren, dass diese API zuerst aufgerufen wird, bevor das Formular den traditionellen POST ausführen kann.

Dazu können Sie Code implementieren, der dem ähnelt, den ich hier geschrieben habe:

$('#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
quelle
1
"Anstatt die Postleitzahl des Back-End-Formulars zu durchsuchen" ... Tatsächlich müssen Sie dies trotzdem tun, und Sie können sich nicht allein auf die clientseitige Validierung verlassen.
Diego V
18

Sie können so etwas tun

$(this).unbind('click').click();
Rafael Oliveira
quelle
Dies ist eine wirklich schöne Lösung - scheint aber nicht auf IE10 / 11 zu funktionieren; (
JonB
47
Warum hast du das Wort "Schmerz" zensiert?
Sean Kendle
Sie haben den Klick ausgelöst, können aber erneut klicken?
Tom Anderson
16

Überschreiben Sie die Eigenschaft isDefaultPreventedwie folgt :

$('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 ist dies die vollständigste Möglichkeit, das Ereignis mit genau denselben Daten erneut auszulösen.

Tomislav Simić
quelle
eist nicht definiert. sollte sein evt.preventDefault(). Ich habe versucht zu bearbeiten, aber meine Änderungen müssen> 6 Zeichen sein und ich habe gerade 2 :(
kevnk
3
@kevnk, ich füge normalerweise eine kurze Beschreibung der Bearbeitung in Form eines Zeilenkommentars hinzu. Dies sollte die Anzahl der übermittelten Zeichen erhöhen.
Rekurs
Ich weiß nicht, warum diese Antwort nicht mehr positiv bewertet wurde. Das ist wirklich nützlich. Funktioniert auch mit Ausbreitungsstopps event.isPropagationStopped = function(){ return false; };. Ich habe dem Ereignis auch eine benutzerdefinierte Eigenschaft hinzugefügt, damit ich im Handler erkennen kann, ob die Prüfung, die die Aktion verhindert hat, ausgeführt wurde, sodass sie nicht erneut ausgeführt wird. Toll!
Kaddath
Ich habe für Bootstrap 4 Tabs verwendet. Es hat einwandfrei funktioniert. Danke vielmals. $ ('# v-pills-tab a'). on ('click', Funktion (e) {e.preventDefault (); setTimeout (function () {e.isDefaultPrevented = function () {return false;} $ ( '# v-pills-home-tab'). on ('gezeigt.bs.tab', function () {$ ('. mainDashboard'). show (); $ ('# changePlans'). hide (); });}, 1000); $ (this) .tab ('show');});
Surya R Praveen
8

Es ist möglich , die Verwendung currentTargetder event. Das Beispiel zeigt, wie Sie mit dem Senden des Formulars fortfahren. Ebenso könnten Sie Funktion von onclickAttribut usw. erhalten.

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

  // code

  event.currentTarget.submit();
});
Salvis Blūzma
quelle
Senden
Wenn Sie submit()dasselbe Element aufrufen , kehren Sie dann nicht zu Ihrem Code "$ (" form "). on (" submit ") zurück und wiederholen ihn immer wieder?
Fanie Void
7

Nur nicht e.preventDefault();oder nur bedingt ausführen.

Sie können sicherlich nicht ändern, wann die ursprüngliche Ereignisaktion auftritt.

Wenn Sie das ursprüngliche UI-Ereignis einige Zeit später "neu erstellen" möchten (z. B. im Rückruf für eine AJAX-Anfrage), müssen Sie es nur auf eine andere Weise vortäuschen (wie in der Antwort von vzwick) ... obwohl ich es tun würde die Verwendbarkeit eines solchen Ansatzes in Frage stellen.

Leichtigkeitsrennen im Orbit
quelle
3

Solange "viele Dinge" nichts Asynchrones tun, ist dies absolut unnötig. Das Ereignis ruft jeden Handler auf seinem Weg nacheinander auf. Wenn also ein Onklick-Ereignis für ein übergeordnetes Element vorliegt, wird dies nach dem Onclik ausgelöst. Ereignis des Kindes hat vollständig verarbeitet. Javascript führt hier kein "Multithreading" durch, wodurch das "Stoppen" der Ereignisverarbeitung erforderlich wird. Fazit: Das "Anhalten" eines Ereignisses, nur um es im selben Handler fortzusetzen, macht keinen Sinn.

wenn „viele Sachen“ ist etwas asynchron dies macht auch keinen Sinn , da sie die asynchronen Dinge verhindert zu tun , was sie tun sollen (asynchrone Sachen) und machen sie bahave wie alles in der Reihenfolge ist (wo wir zu meinem ersten Absatz zurückkommen )

oezi
quelle
Der Prozess in der Mitte ist asynchron, ich möchte das Ergebnis im
Ajax-
1
Wenn Sie auf eine Ajax-Anfrage warten müssen, machen Sie sie synchron (für jquery gibt es die async-fag: api.jquery.com/jQuery.ajax ) ... aber eine synchrone Ajax-Anfrage ist in fast jedem Fall eine schlechte Idee. Es wäre also besser, eine andere Lösung zu finden.
Oezi
3

Der Ansatz, den ich benutze, ist folgender:

$('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
quelle
2

Die akzeptierte Lösung funktioniert nicht, wenn Sie mit einem Ankertag arbeiten. In diesem Fall können Sie nach dem Anruf nicht mehr auf den Link klicken e.preventDefault(). Dies liegt daran, dass das von jQuery generierte Klickereignis nur über nativen Browserereignissen liegt. Das Auslösen eines Klickereignisses auf einem Ankertag folgt also nicht dem Link. Stattdessen können Sie eine Bibliothek wie jquery-simulate verwenden , mit der Sie native Browserereignisse starten können.

Weitere Details dazu finden Sie unter diesem Link

Miguel Carvajal
quelle
1

Eine andere Lösung besteht darin, window.setTimeout im Ereignis-Listener zu verwenden und den Code auszuführen, nachdem der Prozess des Ereignisses abgeschlossen ist. Etwas wie...

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

Ich benutze 0 für den Zeitraum, da mir das Warten egal ist.

Αλέκος
quelle
1

Ich weiß, dass dieses Thema alt ist, aber ich denke, ich kann dazu beitragen. Sie können das Standardverhalten eines Ereignisses für ein bestimmtes Element jederzeit in Ihrer Handlerfunktion auslösen, wenn Sie dieses Verhalten bereits kennen. Wenn Sie beispielsweise das Klickereignis auf der Schaltfläche "Zurücksetzen" auslösen, rufen Sie die Rücksetzfunktion in der nächstgelegenen Form als Standardverhalten auf. In Ihrer Handler-Funktion können Sie nach Verwendung der PreventDefault-Funktion das Standardverhalten aufrufen, indem Sie die Reset-Funktion auf dem nächstgelegenen Formular in Ihrem Handler-Code aufrufen.

Patrik
quelle
0

Wenn dieses Beispiel helfen kann, wird bei einigen Links ein "benutzerdefiniertes Bestätigungs-Popin" hinzugefügt (ich behalte den Code "$ .ui.Modal.confirm", dies ist nur ein Beispiel für den Rückruf, der die ursprüngliche Aktion ausführt):

//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
quelle