How to add a 'submitHandler' function when using jQuery Unobtrusive Validation?

The unobtrusive library will attach the validator on all forms, so you have to replace the submitHandler this way:

$("form").data("validator").settings.submitHandler = function (form) { alert('submit'); form.submit(); };

I found this question while looking for a way to set the invalidHandler, as opposed to the submitHandler. Interestingly, you can't set invalidHandler in the same way when using unobtrusive validation.

The function is not fired when an invalid form is detected:

$("form").data("validator").settings.invalidHandler = function (form, validator) {
    alert('invalid!');
};

This approach works:

$("form").bind("invalid-form.validate", function () {
  alert('invalid!');
});

It seems to be due to the way that the jquery.validate.unobtrusive.js script initialises the Validate plugin, and the way that the plugin invokes the handler.


The following findings may be of interest to understand the difference between :

submitHandler : Called when the form is validated successfully - right before the server postback
invalidHandler : Called if validation fails and there is no post back

@DGreen's solution definitely seems to be the best way to inject special handling if the form validation fails and you need to do something such as display a pop-up validation summary (invalidHandler)

$("form").bind("invalid-form.validate", function () {
  alert('invalid!');
});

@PeteHaus solution is the best way to do something if you want to prevent postback of a successfully validated form (submitHandler)

$("form").data("validator").settings.submitHandler = function (form) { 
                                                     alert('submit'); form.submit(); };

However I was slightly concerned about what behavior I was overriding (or not overriding) by binding to the .validate method like this - and why I had to do each way differently. So I checked the source. I strongly recommend anybody who wants to understand this procedure more does so too - put in some 'alert' statements or 'debugger' statements and it's pretty easy to follow along*

Anyway it turns out that when the jquery.validate.unobtrusive handler initializes jquery.validate plugin it does so in the parseElement() method by retrieving options created by the validationInfo() method.

ValidationInfo() returns the options like this:

 options: {  // options structure passed to jQuery Validate's validate() method
             errorClass: "input-validation-error",
             errorElement: "span",
             errorPlacement: $.proxy(onError, form),
             invalidHandler: $.proxy(onErrors, form),
             messages: {},
             rules: {},
             success: $.proxy(onSuccess, form)
           },

The onErrors() method in jquery.validate.unobtrusive is responsible for dynamically creating the validation summary panel for MVC. If you're not creating a validation summary panel (with @Html.ValidationSummary() which must incidentally be contained within the FORM body) then this method is completely inert and does nothing so you don't need to worry.

function onErrors(event, validator) {  // 'this' is the form elementz
    var container = $(this).find("[data-valmsg-summary=true]"),
        list = container.find("ul");

    if (list && list.length && validator.errorList.length) {
        list.empty();
        container.addClass("validation-summary-errors").removeClass("validation-summary-valid");

        $.each(validator.errorList, function () {
            $("<li />").html(this.message).appendTo(list);
        });
    }
}

If you really want to you can unbind the jquery.validate.unobtrusive handler like this - but i wouldn't bother myself

 $("form").unbind("invalid-form.validate"); // unbind default MS handler

If you're wondering why this works

 $("form").data("validator").settings.submitHandler = ...

and this doesn't work

 $("form").data("validator").settings.invalidHandler = ...

it's because submitHandler is explicily called inside jquery.validate when validation is performed. Therefore it doesn't matter at what point it is set.

 validator.settings.submitHandler.call( validator, validator.currentForm );

but the invalidHandler is bound to an event during init() like this so if you set it in your own code it is too late

if (this.settings.invalidHandler)
    $(this.currentForm).bind("invalid-form.validate", this.settings.invalidHandler);

A little stepping through code makes for a lot of understanding sometimes! Hope this helps

*Make sure you don't have minification in bundles enabled.


I prefer to inject an event handler, so that it can be bound to... :)

//somewhere in your global scripts
$("form").data("validator").settings.submitHandler = function (form) {
    var ret = $(form).trigger('before-submit');
    if (ret !== false) form.submit();
};

This way, I can bind to it anywhere needed...

//in your view script(s)
$("form").bind("before-submit", function() {
    return confirm("Are you sure?");
});

I based this on the original answer, which didn't work for me. I guess things have changed in jQuery.

As usual, take this code and add copious error checking :D I use similar code to prevent double-submits on our forms, and it works fine in IE8+ (as of mvc4 and jquery 1.8.x).

$("#myFormId").confirmAfterValidation();

// placed outside of a $(function(){ scope
jQuery.fn.confirmAfterValidation = function () {
    // do error checking first.  should be null if no handler already
    $(this).data('validator').settings.submitHandler = confirmValidForm;
};

function confirmValidForm(form) {
    var validator = $(this);
    var f = $(form);
    if (confirm("really really submit?")) {
        // unbind so our handler doesn't get called again, and submit
        f.unbind('submit').submit();
    }
        // return value is _ignored_ by jquery.validate, so we have to set
        // the flag on the validator itself, but it should cancel the submit
        // anyway if we have a submitHandler
        validator.cancelSubmit = true;
    }
}