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.