Reputation: 2284
I am using the jQuery Validate plugin, and can never find a good way to display errors for checkboxes. I'd like to highlight all of the checkbox labels in red if none are selected, and decide to do this by adding an error class to the div that contains the checkboxes and checkbox labels. However, it doesn't seem to be adding the class. Am I not selecting the div correctly?
HTML:
<div class="left-aligned indent">
<label id="id_label" for="id" class="label">Items:</label>
<div class="select-group">
<input type="checkbox" name="items" value="1" id="item_1" />
<label class="checkbox">Item #1</label><br />
<input type="checkbox" name="items" value="1" id="item_2" />
<label class="checkbox">Item #2</label><br />
<input type="checkbox" name="items" value="1" id="item_3" />
<label class="checkbox">Item #3</label><br />
</div>
</div>
Javascript:
$().ready(function() {
$('#addForm').validate({
rules: {
"items": {
required: true,
minlength: 1
}
},
errorPlacement: function(error, element) {
if (element.is(':checkbox')) {
$(this).prev("div").addClass('checkbox-error');
}
else {
return true;
}
}
});
});
CSS:
.error {
background-color: #FF898D;
border: 1px solid #000;
}
.checkbox-error {
color: #FF898D;
}
Upvotes: 18
Views: 41153
Reputation: 29829
From my answer on add class to parent div if validation error occurs using jquery validation because I believe it adds value to the existing answers on this question ...
The first order of business is to modify the settings
object on your form's validator. You can do this in any of the following ways:
jQuery.validator.setDefaults()
.validate([options])
$("form").data("validator").settings
Since you're using MVC, option #2 is out of the question since unobtrusive-validation will automatically initialize the form. So let's use option 3 going forward - the goal here is just to be able to customize the settings on the form.
The default methods we'll want to modify are highlight
and unhighlight
which will highlight invalid fields or revert changes made by the highlight option, respectively. Here's their default behavior according to the source code:
highlight: function( element, errorClass, validClass ) {
if ( element.type === "radio" ) {
this.findByName( element.name ).addClass( errorClass ).removeClass( validClass );
} else {
$( element ).addClass( errorClass ).removeClass( validClass );
}
},
unhighlight: function( element, errorClass, validClass ) {
if ( element.type === "radio" ) {
this.findByName( element.name ).removeClass( errorClass ).addClass( validClass );
} else {
$( element ).removeClass( errorClass ).addClass( validClass );
}
}
So you have a couple options here as well.
This route is pretty easy. Just write whatever you want in there. Maybe seed from the source code, maybe do your own thing.
var valSettings = $("form").data("validator").settings
valSettings.highlight = function(element, errorClass, validClass) { ... }
valSettings.unhighlight = function(element, errorClass, validClass) { ... }
This is less intrusive so probably preferable in most cases.
Since ultimately you will be replacing the value of valSettings.highlight
, you'll need access to a clean pristine version of the original function. You can save your own or grab one off the global defaults
// original highlight function
var highlightOriginal = $("form").data("validator").settings.highlight;
var highlightDefaults = $.validator.defaults.highlight
In terms of wrapping JavaScript functions, there are a couple examples here, here, and here). Here's a parsed down example from one of those, that will help bind this
context across function calls, preserve the arity of the arguments being passed, and persist the return value:
function wrap(functionToWrap, beforeFunction) {
return function () {
var args = Array.prototype.slice.call(arguments),
beforeFunction.apply(this, args);
return functionToWrap.apply(this, args);
};
};
Then you'll also have to quickly define whatever additional behavior you want to fire whenever the call is made. In this case, let's find the closest parent div
to the element and update it's classes like this:
function highlightDecorator(element, errorClass, validClass) {
$(element).closest("div").addClass(errorClass).removeClass(validClass);
}
Wrapping It All Up (see what I did there)
$(function () {
var valSettings = $("form").data("validator").settings
valSettings.highlight = wrap($.validator.defaults.highlight, highlightDecorator)
valSettings.unhighlight = wrap($.validator.defaults.unhighlight, unhighlightDecorator)
});
function wrap(functionToWrap, beforeFunction) {
return function () {
var args = Array.prototype.slice.call(arguments);
beforeFunction.apply(this, args);
return functionToWrap.apply(this, args);
};
};
function highlightDecorator(element, errorClass, validClass) {
$(element).closest("div").addClass(errorClass).removeClass(validClass);
}
function unhighlightDecorator(element, errorClass, validClass) {
$(element).closest("div").addClass(validClass).removeClass(errorClass);
}
So when we combine all of the above functions, it should look something like this:
$(function () {
var valSettings = $("form").data("validator").settings
valSettings.highlight = wrap($.validator.defaults.highlight, highlightDecorator)
valSettings.unhighlight = wrap($.validator.defaults.unhighlight, unhighlightDecorator)
});
function wrap(functionToWrap, beforeFunction) {
return function () {
var args = Array.prototype.slice.call(arguments);
beforeFunction.apply(this, args);
return functionToWrap.apply(this, args);
};
};
function highlightDecorator(element, errorClass, validClass) {
$(element).closest("div").addClass(errorClass).removeClass(validClass);
}
function unhighlightDecorator(element, errorClass, validClass) {
$(element).closest("div").addClass(validClass).removeClass(errorClass);
}
input.input-validation-error {
border: solid 1px red;
}
.input-validation-error label {
color: red;
}
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.16.0/jquery.validate.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validation-unobtrusive/3.2.6/jquery.validate.unobtrusive.min.js"></script>
<form action="/Person" method="post">
<div class="required">
<label for="Name">Name <em>*</em></label>
<input id="Name" name="Name" type="text" value=""
data-val="true" data-val-required="The Name field is required."/>
<span class="field-validation-valid"
data-valmsg-for="Name" data-valmsg-replace="true"></span>
</div>
<input type="submit" value="Save" />
</form>
Upvotes: 6
Reputation: 1095
This should work:
$('#myform').validate({
highlight: function(element) {
$(element).parent().addClass("field-error");
},
unhighlight: function(element) {
$(element).parent().removeClass("field-error");
}
});
Checkout the documentation for more options:
Upvotes: 52