I was recently tasked with developing a simple form validation using JavaScript. It took me over 2 hours to finish the task and the Logic was quite simple.
OUR FORM
We will create a basic form with Input Elements and we will give these input elements the attribute myValidator, then we will specify the validation rules inside this attribute.
- R means ( Form field is required ).
- E means ( Form input must be a valid Email address ).
- N means ( Form input must be a valid number ).
<!doctype html>
<html>
<head></head>
<form id="form_register">
<div>
<label>User ID</label>
<input id="inp_uid" type="text" myValidator="R">
</div>
<div>
<label>Email</label>
<input id="inp_email" type="text" myValidator="R,E">
</div>
<div>
<button type="submit">submit</button>
</div>
</form>
</html>
The code below will look for input elements with the attribute myValidator, store the validation rules in a variable then proceeds to validate the form.
document.addEventListener('DOMContentLoaded', function(){
//store validation rules
var validate = {};
(function(){
var myValidators = document.querySelectorAll('input');
//loop through
for(var myv = 0; myv < myValidators.length; ++myv){
//get attr
let attr = myValidators[myv].getAttribute("myValidator");
//get id
let id = myValidators[myv].getAttribute("id");
//get type
let type= myValidators[myv].getAttribute("type");
//syntax {id:["type", "validation rules"]}
//check if validation rules has been built already
if(Object.keys(validate).length !== myValidators.length)
{ validate[id] = [type, attr] };
}
})();
The reason I chose to store the validation rules in a variable, is to prevent users from using the inspect tool to remove the attribute myValidator, which is required for our validations to work.
So this means that as soon as our page loads, it checks for the validation rules present in all input elements, and stores it in a variable with the syntax.
var validate = {
"inp_uid" : ["text", "R"],
"inp_email" : ["text", "R,E"]
};
Our Function
Let's create a parent function that will handle the validation rules. Our return type will be true when all form validations are completed without errors and false when there are validation errors.
We will check if the variable ( validate ), contains input that needs validation, then we will loop through each data and execute the necessary validation.
Remember that if you specified the validation rule as;
- R, it means that the form input is required.
- E, it means that the value of this form input must be a valid email address.
Still, within the loop, we need a variable continueValidation, which checks if a validation rule passed successfully or if a validation rule fails.
If the value of this variable is ( 0 ), it means that a validation rule has failed, so we need to stop further validation until this particular validation passes successfully.
If a validation rule passed successfully, we will increment this variable, so that the next validation rule can be executed.
function myValidate(){
if(validate){
//loop through validations
for(var i = 0; i < Object.keys(validate).length; ++i){
//input id
let index = Object.keys(validate)[i];
//input type
let type =validate[index][0];
//validation rule
let validations = validate[index][1];
//checks if a validation rule passed successfully,
//then continues with the next rule
let continueValidation = 0;
/**check validation rule and validate **/
//Required validation rule
if (validations.includes('R')) {
//retrieve Form Input
let elem = document.querySelector('#'+index);
if(!elem.value) {
myValidateNewError(index, "This Field is required!");
//attach event listener
elem.addEventListener('focusout', function(){
//if error is fixed, remove error
(!elem.value) ?
myValidateNewError(index, "This Field is required!") :
myValidateRemoveError(index);
})//end of focusout
}else{
continueValidation++;
}
}//end of required rule
}//end of loop
return false; //validation failed with form errors
}else{
return true;
}
}
Required Validation Rule
This rule basically checks if the form input contains a value. Using the method .includes() we are able to loop through the validation rules to find a rule that matches ( R ) which stands for required.
Once a match is found, we will query the Input element using its id, then check if the value of this input element is empty.
If the value is empty, we will attach an error to that input element and attach an event listener that will remove the error when the validation completes ssuccessfully.
Email Validation Rule
We have handled the validation rule for required form fields which basically checks if the form input contains a value. Now to perform email validations, we need a regular expression to check for a valid email address.
If the email provided is Invalid, we will append the error to the form and prevent our script from processing the next validation rule by setting the value of the variable continueValidation to zero ( 0 ), until this particular rule passes successfully.
So our Email validation rule will look like this ;
//check if previous validation failed or is successful, then validate this rule
if(continueValidation && validations.includes('E')){
//retrieve form input
let elem = document.querySelector('#'+index);
//use regexp to test input value
if(!(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(elem.value))) {
myValidateNewError(index, "Please provide a Valid Email Address!");
//attach event listener
elem.addEventListener('focusout', function(){
//if error is fixed, remove error
(!(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(elem.value))) ?
myValidateNewError(index, "Please provide a Valid Email Address!") :
myValidateRemoveError(index);
})
//prevent further validation
continueValidation = 0;
}else{
//validation continues with the next rule
continueValidation++;
}
}//email validation rule
Our validation rules
When validating any rule, we attach a focusout event listener to that particular element to automatically revalidate the form input when a value has been provided.
Now, if an error occurred during validation, we will append that error to the form through the function myValidateNewError() and later when the validation executes successfully, we will remove the error through the function myValidateRemoveError().
Here are the functions;
//new error function
function myValidateNewError(inputID, error){
//remove previous error
myValidateRemoveError(inputID);
//add error to element
var g = document.createElement("p");
g.setAttribute("id", "myValidate_"+inputID);
g.setAttribute("class", "myValidate-txt-error");
g.innerText = error; //set the text content as the error
//set class of input error
var f = document.querySelector('#'+inputID);
f.setAttribute("class", "myValidate-inp-error");
//append error to form
f.parentNode.appendChild(g);
}
//remove error function
function myValidateRemoveError(inputID){
//remove error text
let errorElem = (document.querySelector('#myValidate_'+inputID)) ?
document.querySelector('#myValidate_'+inputID) : null;
//remove classlist
(document.querySelector('#'+inputID).classList.value.includes('myValidate-inp-error')) ?
document.querySelector('#'+inputID).classList.remove('myValidate-inp-error') : '';
if(errorElem) errorElem.remove();
}
Test Run
In order to test this script, we need to group the functions together inside the parent function ( myValidate() ), call the function, then check its return value when the submit button is clicked.
If the return value is false, this means that there are validation errors. But if the return value is true, this means that all validations passed successfully and the form is ready to be processed.
Our Script
document.addEventListener('DOMContentLoaded', function(){
//store validation rules
var validate = {};
(function(){
var myValidators = document.querySelectorAll('input');
//loop through
for(var myv = 0; myv < myValidators.length; ++myv){
//get attr
let attr = myValidators[myv].getAttribute("myValidator");
//get id
let id = myValidators[myv].getAttribute("id");
//get type
let type= myValidators[myv].getAttribute("type");
//syntax {id:["type", "validation rules"]}
//check if validation rules has been built already
if(Object.keys(validate).length !== myValidators.length)
{ validate[id] = [type, attr] };
}
})();
function myValidate() {
//new error function
function myValidateNewError(inputID, error){
//remove previous error
myValidateRemoveError(inputID);
//add error to element
var g = document.createElement("p");
g.setAttribute("id", "myValidate_"+inputID);
g.setAttribute("class", "myValidate-txt-error");
g.innerText = error; //set the text content as the error
//set class of input error
var f = document.querySelector('#'+inputID);
f.setAttribute("class", "myValidate-inp-error");
//append error to form
f.parentNode.appendChild(g);
}
//remove error function
function myValidateRemoveError(inputID){
//remove error text
let errorElem = (document.querySelector('#myValidate_'+inputID)) ?
document.querySelector('#myValidate_'+inputID) : null;
//remove classlist
(document.querySelector('#'+inputID).classList.value.includes('myValidate-inp-error')) ?
document.querySelector('#'+inputID).classList.remove('myValidate-inp-error') : '';
if(errorElem) errorElem.remove();
}
if(validate){
//loop through validations
for(var i = 0; i < Object.keys(validate).length; ++i){
//input id
let index = Object.keys(validate)[i];
//input type
let type =validate[index][0];
//validation rule
let validations = validate[index][1];
//checks if a validation rule passed successfully,
//then continues with the next rule
let continueValidation = 0;
/**check validation rule and validate **/
//Required validation rule
if (validations.includes('R')) {
//retrieve Form Input
let elem = document.querySelector('#'+index);
if(!elem.value) {
myValidateNewError(index, "This Field is required!");
//attach event listener
elem.addEventListener('focusout', function(){
//if error is fixed, remove error
(!elem.value) ?
myValidateNewError(index, "This Field is required!") :
myValidateRemoveError(index);
})//end of focusout
}else{
continueValidation++;
}
}//end of required rule
//check if previous validation failed or is successful, then validate this rule
if(continueValidation && validations.includes('E')){
//retrieve form input
let elem = document.querySelector('#'+index);
if(!(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(elem.value))) {
myValidateNewError(index, "Please provide a Valid Email Address!");
//attach event listener
elem.addEventListener('focusout', function(){
//if error is fixed, remove error
(!(/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(elem.value))) ?
myValidateNewError(index, "Please provide a Valid Email Address!") :
myValidateRemoveError(index);
})
//prevent further validation
continueValidation = 0;
}else{
//validation continues with the next rule
continueValidation++;
}
}//end of email validation rule
}//end of loop
return false; //validation failed with form errors
}else{
return true;
}
}
document.querySelector('#form_register').addEventListener('submit', function(e){
e.preventDefault();
//if validation return value = true
if( myValidate() ) {
//form submission here
}else{
// tell user that A form Error has occured
}
});
})
Our HTML
<!doctype html>
<html>
<head></head>
<style>
.error{
color:red;
}
label{
display: block;
}
input{
display: block;
width: 300px;
padding: 10px;
border-radius: 5px;
background-color: #fffefe;
border: 2px solid #cfcfcf;
}
p{
margin-bottom:5px;
}
/** my validations **/
.myValidate-inp-error{
border: 2px solid #e64a61;
}
.myValidate-txt-error{
color : #e64a61;
}
</style>
<form id="form_register">
<div>
<label>User ID</label>
<input id="inp_uid" type="text" myValidator="R">
</div>
<div>
<label>Email</label>
<input id="inp_email" type="text" myValidator="R,E">
</div>
<div>
<button type="submit">submit</button>
</div>
</form>
</html>
Copy the script into a script tag of a blank page and the HTML inside the same page, then try to submit the form without providing values.
This is the result I get
Now if I should enter a value, and leave the form input, the focusOut event is fired and it will clear the form error.
Remember that we added an email validation rule.
If we provide an invalid email address, then try to submit the form, this is the result we get.
I have explained how the script works. You may add your form validation rules and be rest assured that this validation is perfect on the client-side only. You may also need to revalidate on the server-side of your APP.
I have expanded this script with the name octaValidate to support more validation rules. You may contribute to this project by providing your validation rules too.
Check it out on my [GitHub Here] (github.com/Octagon-simon/octaValidate)