I've got the task from my customer not to mark empty non-required fields.
On the other hand I had to combine frontend with backend validation to
prevent hacks and for ease of validation against a database.
Following example is based on BS4, thymeleaf and was inspired by @ksiger's idea in his JSfiddle (see above), but had to be modified to match with my criteria.
So, first lets inspect the form (mind the class):
<form id="myForm" action="#" th:action="@{/formurl}" th:object="${myForm}"
method="post" novalidate class="needs-custom-validation">
For validation, I am working with "is-valid" and "is-invalid" classes only. Let's have a look at the jQuery stuff:
$(function () {
// validate field on change
$(':input').change(function (event) {
var isValid = checkField(this);
// ... I'm doing something here ...
return isValid;
});
// check field validity
checkField = function (fld) {
var isRequired = $(fld).prop('required');
var isValid = $(fld).prop("validity").valid;
var isEmpty = $(fld).val() == "";
$(fld).removeClass("is-valid").removeClass("is-invalid");
if (isValid) {
if (!isEmpty)
$(fld).addClass("is-valid");
return true;
} else {
$(fld).addClass("is-invalid");
return false;
}
}
/* form validation */
checkForm = function (f) {
var isValid = true;
$(f).find("input, textarea, select").each(function () {
isValid = checkField(this) && isValid; // "&&" is shortcut
});
return isValid;
}
$('[class="needs-custom-validation"]').submit(function (event) {
if (!checkForm(this)) {
event.preventDefault();
event.stopPropagation();
return false;
}
// call ajax here or let it bubble to the submit button
});
});
That's the whole magic.