Search code examples
jqueryvalidationmaskedinput

Conflict between jQuery Validate and Masked Input


I've got a form that's using both jQuery Validate and Masked Input for phone number and US zip code fields.

For example, for a US zip code, Masked Input allows only numbers to be entered, and forces either the format "99999" or "99999-9999" (where 9 can be any number). The Validate rule requires the same. But in some cases, Validate marks a field as invalid when it should actually be valid.

Code Specifics

The regex that jQuery Validate is using on the zip code field is ^\d{5}$|^\d{5}\-\d{4}$.

The mask I'm applying with Masked Input is .mask('99999?-9999')

Steps to reproduce

A conflict between them is produced when I do the following:

  • Fill out an invalid zip code (say, three digits)
  • Tab away from the field. Masked Input erases the input (expected behavior) and jQuery Validate marks it as invalid because it's blank (also expected).
  • Go back to the zip field and fill in a valid 5-digit zip.
  • Tab away from the field. It is still marked as invalid. This is unexpected.

This problem does not happen if I fill out a 9-digit zip.

Hypothesis

I think this error is because in the case of the 5-digit zip, Masked Input has temporarily inserted "-____" to show the user that they can optionally enter a dash and four more digits. This is removed on blur, but before it's removed, the field is validated and fails, since underscores aren't permitted.

This hypothesis is supported by the fact that, if the form is subsequently re-validated, the zip field will pass. I have done this two ways:

  • By submitting the form; all fields are re-validated when the submit button is clicked, the zip field passes, and the form submits.

  • By setting up a blur event that re-validates that specific field. For example:

    $("#zipcode").blur(function(){ $(this).closest('form').validate().element($(this)); });

This can serve as a hacky solution, but isn't very satisfactory, because 1) the default settings already re-validate on blur, so this is repetitive, and 2) it requires additional code besides the normal Validate rules.

Has anybody else run into this issue? Do you have a more elegant solution than setting up an additional blur event listener?

Update: applying the hacky solution

Even applying the hacky solution above doesn't work as nicely as I'd like. For instance, this doesn't work:

$appDiv.delegate('input,select,textarea','blur',function(){
  $(this).closest('form').validate().element($(this));
});

...nor does this:

$('input,select,textarea').live('blur',function(){
  $(this).closest('form').validate().element($(this));
});

...but this does:

$('input,select,textarea').each(function(){
  $(this).blur(function(){
    $(this).closest('form').validate().element($(this));
  });
});

Since these elements are loaded by AJAX, the .each version has to be run each time a form section is loaded.


Solution

  • 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.