How to trigger an event after using event.preventDefault()

非 Y 不嫁゛ 提交于 2020-01-18 03:45:26

问题


I want to hold an event until I am ready to fire it e.g

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

   e.preventDefault(); 

   // do lots of stuff

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

}

Is there an equivalent to the run() function described above?


回答1:


Nope. Once the event has been canceled, it is canceled.

You can re-fire the event later on though, using a flag to determine whether your custom code has already run or not - such as this (please ignore the blatant namespace pollution):

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

A more generalized variant (with the added benefit of avoiding the global namespace pollution) could be:

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-minimalistic jQuery plugin with 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!');
    });



回答2:


A more recent version of the accepted answer.

Brief 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 */
    }

});



A good use case for something like this is where you may have some legacy form code that works, but you've been asked to enhance the form by adding something like email address validation before submitting the form. Instead of digging through the back-end form post code, you could write an API and then update your front-end code to hit that API first before allowing the form to do it's traditional POST.

To do that, you can implement code similar to what I've written here:

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

    }

});



回答3:


You can do something like

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



回答4:


Override the property isDefaultPrevented like this:

$('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, this is most complete way of retriggering the event with the exactly same data.




回答5:


It is possible to use currentTarget of the event. Example shows how to proceed with form submit. Likewise you could get function from onclick attribute etc.

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

  // code

  event.currentTarget.submit();
});



回答6:


Just don't perform e.preventDefault();, or perform it conditionally.

You certainly can't alter when the original event action occurs.

If you want to "recreate" the original UI event some time later (say, in the callback for an AJAX request) then you'll just have to fake it some other way (like in vzwick's answer)... though I'd question the usability of such an approach.




回答7:


A more recent answer skillfully uses jQuery.one()

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

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

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




回答8:


as long as "lots of stuff" isn't doing something asynchronous this is absolutely unneccessary - the event will call every handler on his way in sequence, so if theres a onklick-event on a parent-element this will fire after the onclik-event of the child has processed completely. javascript doesn't do some kind of "multithreading" here that makes "stopping" the event processing neccessary. conclusion: "pausing" an event just to resume it in the same handler doesn't make any sense.

if "lots of stuff" is something asynchronous this also doesn't make sense as it prevents the asynchonous things to do what they should do (asynchonous stuff) and make them bahave like everything is in sequence (where we come back to my first paragraph)




回答9:


The approach I use is this:

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



回答10:


The accepted solution wont work in case you are working with an anchor tag. In this case you wont be able to click the link again after calling e.preventDefault(). Thats because the click event generated by jQuery is just layer on top of native browser events. So triggering a 'click' event on an anchor tag wont follow the link. Instead you could use a library like jquery-simulate that will allow you to launch native browser events.

More details about this can be found in this link




回答11:


Another solution is to use window.setTimeout in the event listener and execute the code after the event's process has finished. Something like...

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

I use 0 for the period since I do not care about waiting.




回答12:


I know this topic is old but I think I can contribute. You can trigger the default behavior of an event on a specific element any time in your handler function if you already know that behavior. For example, when you trigger the click event on the reset button, you actually call the reset function on the closest form as the default behavior. In your handler function, after using the preventDefault function, you can recall the default behavior by calling the reset function on the closest form anywhere in your handler code.




回答13:


If this example can help, adds a "custom confirm popin" on some links (I keep the code of "$.ui.Modal.confirm", it's just an exemple for the callback that executes the original action) :

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


来源:https://stackoverflow.com/questions/7610871/how-to-trigger-an-event-after-using-event-preventdefault

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!