How to trigger an event after using event.preventDefault()
You can do something like
$(this).unbind('click').click();
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.
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!');
});
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();
}
});