Quantcast
Viewing latest article 16
Browse Latest Browse All 101

Creating a reusable JavaScript form validation class

Just recently I was looking at some of my old code. For a website build I had used HTML5 form validation with a JavaScript fallback. The form itself was a long one and I had created a module that saved the individual form values into an objects memory which was then validated in the next method. Something like this:

contactQaStudio: (_private.form.contactQaStudio.checked) ? _private.form.contactQaStudio.value : '',
contactQaDirect: (_private.form.contactQaDirect.checked) ? _private.form.contactQaDirect.value : '',
contactStarMine: (_private.form.contactStarMine.checked) ? _private.form.contactStarMine.value : '',

This is fine if you have a small form but when you have 32 separate fields it creates non-elegant code. To be honest I felt slightly embarrassed and I'm sure that whoever maintains this script will be cursing the day I was born.

The best solution would be one where you drop in a reusable JavaScript class and access it like so:

// name attribute of form
var contactForm = new FormClass('contactUs');

// id attribute of form elements
// use required if the form field is essential
contactForm.required('contact-name', 'Please fill in your contact name');
contactForm.required('contact-email', 'Please fill in your email address');

// id attribute of form elements
// use validationInd to create individual form field validation
contactForm.validationInd('contact-email', 'email');
contactForm.validationInd('contact-name', 'Please enter a minimum of 10 characters for a contact name');

// use validation to run validation function
// will produce error messages or save form if okau
contactForm.validation();

This is just what I have done. My aim was to leverage the composite pattern, but in the end I fell short of that. I liberally use the prototype keyword in my script. To understand how, why and when this should be used will make you are far better equipped JavaScript coder. If you don't then I recommend that you read Ross Harmes and Dustin Diaz's book Pro-JavaScript Design Patterns.

Using the above method the id of the form field and the error message is added to an object, which is then looped through.

Individual form validation is handled with the help of regex. For instance, a coder writes 'Please enter a minimum of 10 characters for a contact name'. In the loop if the word 'minimum' is found in the string then the appropriate statement is run with the number of characters, in this case 10, extracted with regex, just like this:

if (this.validationIndValues[key].match(/minimum/g)) {

    // check if number of characters is less than specified

    if (parseInt(doc.querySelector('input#' + key).value.length) < parseInt(this.validationIndValues[key].match(/d+.?d*/g))) {

        anArray.push(this.validationIndValues[key]);

    }

}

By building up the user-specified form objects it is then necessary to run the validation on form submit:

addEvent(document.forms[this.formName], 'submit', validForm.bind(this));

Note the use of the bind() function. This is essential to bypass JavaScript function scope so that the this object is passed into the validForm() function and can then be accessed directly.

Checking for empty values is straight forward. It loops through the id attributes in the object, running a check for empty values on the required form fields:

// loop through ruleList object literals
if (this.formRequired.hasOwnProperty(key)) {

    // check if any of the required values are empty

    if (doc.querySelector('input#' + key).value === '') {

        anArray.push(this.formRequired[key]);

    } // end if

} // end if

The error message is passed into an array which is then added to the errorMessage() method afterwards.

Individual form field validation is a little more complex but essentially follows the same pattern:

for (key in this.validationIndValues) {

    // loop through ruleList object literals
    if (this.validationIndValues.hasOwnProperty(key)) {

        if (this.validationIndValues[key].match(/email/g)) {

            // check if valid email address

            if (!doc.querySelector('input#' + key).value.match(/^(.+)@([^();:,<>]+.[a-zA-Z]{2,4})/)) {

                anArray.push('You have entered an incorrect email address');

            }

        }

        if (this.validationIndValues[key].match(/minimum/g)) {

            // check if number of characters is less than specified

            if (parseInt(doc.querySelector('input#' + key).value.length) < parseInt(this.validationIndValues[key].match(/d+.?d*/g))) {

                anArray.push(this.validationIndValues[key]);

            }

        }

        if (this.validationIndValues[key].match(/maximum/g)) {

            // check if number of characers is more than specified

            if (parseInt(doc.querySelector('input#' + key).value.length) > parseInt(this.validationIndValues[key].match(/d+.?d*/g))) {

                anArray.push(this.validationIndValues[key]);

            }

        }

    }

This is fine for text or textarea fields, but validating checkboxes or radio buttons would require something different.

Conclusion

It works in all browser apart from IE7 because it uses querySelector. However, it should be fairly painless to substitute querySelector with a JS library DOM transversal interface.

What I don't like about the code is that the validation function is too fat and needs to be broken down. It's good JS practice to keep individual methods / functions as thin as possible with each only serving one purpose. It makes the code more readable and it's easier for debugging and unit testing.

Still, it's a good starting point and it's code I will return to in the future when form validation rears its head again.

The code can be found on this GitGist.


Viewing latest article 16
Browse Latest Browse All 101

Trending Articles