Search code examples
jqueryjquery-events

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


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?


Solution

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