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