jQuery Validate - require at least one field in a group to be filled
- by Nathan Long
I'm using the excellent jQuery Validate Plugin to validate some forms. On one form, I need to ensure that the user fills in at least one of a group of fields. I think I've got a pretty good solution, and wanted to share it. Please suggest any improvements you can think of.
Finding no built-in way to do this, I searched and found Rebecca Murphey's custom validation method, which was very helpful.
I improved this in three ways:
To let you pass in a selector for the group of fields
To let you specify how many of that group must be filled for validation to pass
To show all inputs in the group as passing validation as soon as one of them passes
validation.
So you can say "at least X inputs that match selector Y must be filled."
The end result is a rule like this:
partnumber: {
require_from_group: [2,".productinfo"]
}
//The partnumber input will validate if
//at least 2 `.productinfo` inputs are filled
For best results, put this rule AFTER any formatting rules for that field (like "must contain only numbers", etc). This way, if the user gets an error from this rule and starts filling out one of the fields, they will get immediate feedback about the formatting required without having to fill another field first.
Item #3 assumes that you're adding a class of .checked to your error messages upon successful validation. You can do this as follows, as demonstrated here.
success: function(label) {
label.html(" ").addClass("checked");
}
As in the demo linked above, I use CSS to give each span.error an X image as its background, unless it has the class .checked, in which case it gets a check mark image.
Here's my code so far:
jQuery.validator.addMethod("require_from_group", function(value, element, options) {
// From the options array, find out what selector matches
// our group of inputs and how many of them should be filled.
numberRequired = options[0];
selector = options[1];
var commonParent = $(element).parents('form');
var numberFilled = 0;
commonParent.find(selector).each(function(){
// Look through fields matching our selector and total up
// how many of them have been filled
if ($(this).val()) {
numberFilled++;
}
});
if (numberFilled >= numberRequired) {
// This part is a bit of a hack - we make these
// fields look like they've passed validation by
// hiding their error messages, etc. Strictly speaking,
// they haven't been re-validated, though, so it's possible
// that we're hiding another validation problem. But there's
// no way (that I know of) to trigger actual re-validation,
// and in any case, any other errors will pop back up when
// the user tries to submit the form.
// If anyone knows a way to re-validate, please comment.
//
// For imputs matching our selector, remove error class
// from their text.
commonParent.find(selector).removeClass('error');
// Also look for inserted error messages and mark them
// with class 'checked'
var remainingErrors = commonParent.find(selector)
.next('label.error').not('.checked');
remainingErrors.text("").addClass('checked');
// Tell the Validate plugin that this test passed
return true;
}
// The {0} in the next line is the 0th item in the options array
}, jQuery.format("Please fill out at least {0} of these fields."));
Questions? Comments?