Today we are going to validate our form using the constraint validation API and display custom messages for validation errors. Let’s get started:
<form novalidate>
<div class="form-row">
<div class="form-group col-md-6">
<label for="username">Username</label>
<input type="text" id="username" class="form-control" required />
<span class="error" aria-live="polite"></span>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-6">
<label for="email">Email</label>
<input
type="email"
class="form-control"
required
id="email"
minlength="8"
/>
<span class="error" aria-live="polite"></span>
</div>
</div>
<button type="submit" class="btn btn-primary">Sign in</button>
</form>span is used for display validation errors: <span class="error" aria-live="polite"></span>As you might seen above, we are using two ordinary inputs and two span elements for validation inputs.
:valid css pseudo-class:invalid css pseudo-classDepending on which validator you use, corresponding pseudo-class will be activated for that particular element.
input:invalid {
border-color: #200;
background-color: #fdd;
}
input:valid {
background-color: rgb(0, 206, 0);
color: #fff;
}
input:focus:invalid {
outline: none;
}Query for input elements:
const form = document.getElementsByTagName("form")[0];
const email = document.getElementById("email");
const username = document.getElementById("username");
const emailError = document.querySelector("#email + span.error");
const usernameError = document.querySelector("#username + span.error");Attach input events for both email and username:
addInputEventListener.call(email, "input", emailError, showError);
addInputEventListener.call(username, "input", usernameError, showError);Handle input event for both email and username by using
a single function.
function addInputEventListener(onEvent, errorElement, errorCallback) {
this.addEventListener(onEvent, ev => {
if (this.validity.valid) {
errorElement.className = "";
errorElement.innerHTML = "";
} else {
errorCallback(this.name);
}
});
}Add the handler for submitting the form:
form.addEventListener("submit", function(event) {
showError("email");
showError("username");
event.preventDefault();
});After everything is set, we have to implement our customized error messages:
function buildErrorMessage(element, errorMessagesMap) {
const errors = [];
for (const prop in element.validity) {
if (element.validity[prop]) {
errors.push(errorMessagesMap[prop]);
}
}
return { element, errors };
}
function setErrorMessage({ element, errors }) {
element.innerHTML = errors.join("<br/> ");
}
function errorOrEmptyClass({ element, errors }) {
element.className = errors.length > 0 ? "error active" : "";
}
function showError(name) {
const nameToErrorInput = {
email: emailError,
username: usernameError
};
const validator = {
username: element => ({
...buildErrorMessage(username, {
valueMissing: "Username is required."
}),
element
}),
email: element => ({
...buildErrorMessage(email, {
valueMissing: "Email is required.",
typeMismatch: "Value needs to be an email address.",
tooShort: `Email to short you need ${
email.minLength
} characters. You entered ${email.value.length}.`
}),
element
})
};
[validator[name](nameToErrorInput[name])].map(setErrorMessage);
[validator[name](nameToErrorInput[name])].map(errorOrEmptyClass);
}HTML form elements implement the following validation interface.
The Constraint validation API helps us by providing the validity property and other useful methods,
but we’ve done our validation just by using the validity property.
validity property contains getters for checking exact which aspect of validation has just failed.
Example:
required validation:valueMissing getter will return true when no value is provided.username.validity.valueMissing // true