Simple Form Validation Using JavaScript

Simple Form Validation Using JavaScript

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

image.png

Now if I should enter a value, and leave the form input, the focusOut event is fired and it will clear the form error.

image.png

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.

image.png

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)