Conflict between jQuery Validate and Masked Input
I was actually looking for an answer to this exact question. Ended up figuring out a more reliable workaround.
Since I am defining my own validator method for the zipcode I modified it so that it would remove the hyphen if the length of the zipcode was 6 after I removed the placeholder from the value.
It looked like this:
$.validator.addMethod("zipcode", function(postalcode, element) {
//removes placeholder from string
postalcode = postalcode.split("_").join("");
//Checks the length of the zipcode now that placeholder characters are removed.
if (postalcode.length === 6) {
//Removes hyphen
postalcode = postalcode.replace("-", "");
}
//validates postalcode.
return this.optional(element) || postalcode.match(/^\d{5}$|^\d{5}\-\d{4}$/);
}, "Please specify a valid zip code");
So the postalcode I am validating will have the placeholders added by the input plugin and the hyphen removed if the zipcode that is entered only has 5 numerical digits (6 including the hyphen). So it will validate it properly.
I had a similar problem and managed to solve it changing the default placeholder character to an empty string.
I was using a .mask('9?99');
and was validating with a simple 'number' rule, and was having the same kind of conflict.
I changed the mask declaration to .mask('9?99',{placeholder:''});
and... no more conflict. :)
There's a possible problem in your case, because of the -
in the zip code, which is always inserted in the form by the mask plugin. I think you can change the regexp to ^\d{5}\-$|^\d{5}\-\d{4}$
(matching the dash in both cases) and it should validate then.
Anyway, you posted a while ago and probably don't need this anymore but perhaps it'll help someone else. :)
I tried following solution, and it works like a charm.
$("[data-input-type=phone]", "body")
.mask("(999) 999 99 99")
.bind("blur", function () {
// force revalidate on blur.
var frm = $(this).parents("form");
// if form has a validator
if ($.data( frm[0], 'validator' )) {
var validator = $(this).parents("form").validate();
validator.settings.onfocusout.apply(validator, [this]);
}
});
What triggers the problem is event ordering.
When an element is masked, it is validated by maskedinput plugin on blur
, but same element is validated by validator plugin on focusout
event (which is a wrapper for blur on non-ie browsers) which is called before maskedinput's blur.
In this situation input element has the value "(___) ___ __ __"
when validator checks for the value. When code reaches maskedinput's blur event, plugin tests and clears the value since it is not a valid input.
Validation result may be different for each validation case. For instance required
rules will pass with success since element has a value. non-required number
fields will fail even if we leave the input empty since a mask like "999"
may be tested as 12_
the code above tests if form of masked input has validation attached to it, and recalls focusout event handler. Since our handler is attached as the latest, hopefully it will be called at last.
A warning, code simply copies behavior of validation plugin. It will probably work for a decade but may fail if validation plugin decides to do things different than now.
sincerely
Simply hook in to the mask() function and append some additional logic to fire your own blur logic after the default mask's blur logic:
//Store the original mask function
var origMaskFn = $.fn.mask;
$.fn.mask = function (mask, settings) {
//Call the original function applying the default settings
origMaskFn.apply(this, [mask, settings]);
//Manually validate our element on blur to prevent unobtrusive messages
//from showing before the mask is properly scrubbed
$(this).bind('blur', function () {
var $form = $(this).parents('form');
if (!$form.exists())
return;
var validator = $form.validate().element($(this));
});
}
Also, you may notice the 'exists()' function. This is something I use a lot with my jQuery selectors:
//Function to detect if a jQuery elements exists. i.e.:
// var $element = $(selector);
// if($element.exists()) do something...
$.fn.exists = function () {
return this.length !== 0;
}