<html>
<head>
<title>JavaScript Form Validation</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div>
<form method="post" action="" id="test-form" enctype="multipart/form-data">
<p>
<label><strong>Full Name</strong>
<input type="text" name="fullname" id="fullname" value="" placeholder="Full Name" />
</label>
</p>
<p>
<label><strong>Date of Birth</strong>
<input type="text" name="date_of_birth" id="date_of_birth" value="" placeholder="2016-12-31" />
</label>
</p>
<p>
<label><strong>Age</strong>
<input type="text" name="age" id="age" value="" placeholder="25" />
</label>
</p>
<p>
<label><strong>Height</strong>
<input type="text" name="height" id="height" value="" placeholder="6.5" />
</label>
</p>
<p>
<label><strong>Email</strong>
<input type="text" name="email" id="email" value="" placeholder="test@example.com" />
</label>
</p>
<p>
<label><strong>Password</strong>
<input type="password" name="password" id="password" value="" />
</label>
</p>
<p>
<label><strong>Confirm Password</strong>
<input type="password" name="rpt_password" id="rpt_password" value="" />
</label>
</p>
<p>
<label><strong>Message</strong>
<textarea name="message" id="message" placeholder="Message"></textarea>
</label>
</p>
<p>
<label><strong>Photo</strong>
<input type="file" name="photo" id="photo" />
</label>
</p>
<p>
<label><strong>Gender</strong>
<input type="radio" name="gender" id="male" value="male" /> Male
<input type="radio" name="gender" id="female" value="female" /> Female
</label>
</p>
<p>
<strong>Color</strong>
<input type="checkbox" name="color[]" id="red" value="red" /> Red
<input type="checkbox" name="color[]" id="green" value="green" /> Green
<input type="checkbox" name="color[]" id="blue" value="blue" /> Blue
<input type="checkbox" name="color[]" id="white" value="white" /> White
<input type="checkbox" name="color[]" id="black" value="black" /> Black
</p>
<p>
<label><strong>Country</strong>
<select name="country" id="country">
<option value="">Select Country</option>
<option value="uganda">Uganda</option>
<option value="kenya">Kenya</option>
<option value="tanzania">Tanzania</option>
<option value="rwanda">Rwanda</option>
<option value="burundi">Burundi</option>
<option value="south_sudan">South Sudan</option>
</select>
</label>
</p>
<p>
<input type="submit" name="submit" id="submit" value="Submit" />
</p>
</form>
</div>
</body>
</html>
function trim( val ) {
'use strict';
if ( typeof String.prototype.trim === 'function' ) {
return String(val).trim();
} else {
return String(val).replace(/^\s+|\s+$/g, '');
}
}
function addEvent( obj, type, fn ) {
'use strict';
if (obj.attachEvent) {
obj['e' + type + fn] = fn;
obj[type + fn] = function () {
obj['e' + type + fn](window.event);
};
obj.attachEvent('on' + type, obj[type + fn]);
} else {
obj.addEventListener(type, fn, false);
}
}
function removeEvent( obj, type, fn ) {
'use strict';
if (obj.detachEvent) {
obj.detachEvent('on' + type, obj[type + fn]);
obj[type + fn] = null;
} else {
obj.removeEventListener(type, fn, false);
}
}
function isEmpty( obj ) {
'use strict';
var count = 0;
obj = obj || {};
for ( var prop in obj ) {
if ( {}.hasOwnProperty.call(obj, prop) ) {
count += 1;
}
}
return ( count === 0 ) ? true : false;
}
function Validation( items ) {
'use strict';
this.items = items || {};
this.errors = {};
this.data = {};
this.success = false;
}
Validation.prototype.check = function( form ) {
'use strict';
for ( var elem in (this.items) ) {
if ( {}.hasOwnProperty.call(this.items, elem) ) {
var rules = this.items[elem];
var formElement = form[elem];
// Is it an instance of radionodelist or not
// As in is a radio or checkbox
if ( 'tagName' in formElement ) {
switch ( formElement.tagName.toLowerCase() ) {
case 'select':
this.validateSelect( elem, rules, formElement, form, this.items );
break;
case 'input':
case 'textarea':
default:
this.validateInput( elem, rules, formElement, form, this.items );
break;
}
} else {
var firstNode = formElement[0]; // FormElement contains a RadioNodeList Object
switch ( firstNode.getAttribute('type').toLowerCase() ) {
case 'radio':
this.validateRadio( elem, rules, formElement, form, this.items );
break;
case 'checkbox':
this.validateCheckBox( elem, rules, formElement, form, this.items );
break;
}
}
}
}
if ( isEmpty(this.errors) ) {
this.success = true;
}
return this;
};
// Validate provided value, if any
Validation.prototype.validateValue = function( value, elem, rules, form, items ) {
'use strict';
var error = false;
if ( 'required' in rules ) {
if ( value.length === 0 ) {
this.errors[elem] = rules['name'] + ' is required';
error = true;
}
}
if ( (value.length !== 0) && !(error) ) {
if ( 'min' in rules ) {
if ( rules['expected'].toLowerCase() === 'integer' ) {
// Check if a valid number or integer is provided
if ( isNaN(Number(value)) ) {
this.errors[elem] = rules['name'] + ' is not a valid number';
error = true;
}
}
if ( !(error) ) {
if ( this.checkMin(value, rules['min'], rules['expected']) ) {
this.errors[elem] = this.getMinMessage(
rules['name'], rules['min'], rules['expected']
);
error = true;
}
}
}
}
if ( (value.length !== 0) && !(error) ) {
if ( 'max' in rules ) {
if ( rules['expected'].toLowerCase() === 'integer' ) {
// Check if a valid number or integer is provided
if ( isNaN(Number(value)) ) {
this.errors[elem] = rules['name'] + ' is not a valid number';
error = true;
}
}
if ( !(error) ) {
if ( this.checkMax(value, rules['max'], rules['expected']) ) {
this.errors[elem] = this.getMaxMessage(
rules['name'], rules['max'], rules['expected']
);
error = true;
}
}
}
}
if ( (value.length !== 0) && !(error) ) {
if ( 'matches' in rules ) {
var compareValue = form[rules['matches']].value;
if ( value !== compareValue ) {
var compareName = items[ rules['matches'] ];
compareName = compareName['name'];
this.errors[elem] = rules['name'] + ' does not match ' + compareName;
error = true;
}
}
}
if ( (value.length !== 0) && !(error) ) {
if ( 'email' in rules ) {
if ( !(this.checkEmail(value)) ) {
this.errors[elem] = rules['name'] + ' is not valid';
error = true;
}
}
}
if ( (value.length !== 0) && !(error) ) {
if ( 'date' in rules ) {
if ( !(this.checkDate(value)) ) {
this.errors[elem] = rules['name'] + ' is not a valid date';
error = true;
}
}
}
if ( (value.length !== 0) && !(error) ) {
if ( 'permitted' in rules ) {
if ( value.indexOf('fakepath') !== -1 && value.indexOf('\\') !== -1 ) {
value = value.split('\\').pop();
} else if ( value.indexOf('fakepath') !== -1 && value.indexOf('/') !== -1 ) {
value = value.split('/').pop();
}
var permitted = rules['permitted'];
var checkValue = value.split('.').pop().toLowerCase();
if ( permitted.indexOf(checkValue) === -1 ) {
this.errors[elem] = rules['name'] + ' is not permitted';
error = true;
}
}
}
if ( (value.length !== 0) && !(error) ) {
if ( 'pattern' in rules ) {
var regex = rules['pattern'];
if ( !(regex.test(value)) ) {
this.errors[elem] = rules['name'] + ' is not in the required format';
error = true;
}
}
}
// Finally, if no error add to data collection
if ( !(error) ) {
this.data[elem] = value;
}
};
// Validate Select Menu
Validation.prototype.validateSelect = function( elem, rules, formElement, form, items ) {
'use strict';
var value = '';
if ( formElement.hasAttribute('multiple') ) {
value = [];
for ( var i = 0, len = formElement.options.length; i < len; i += 1 ) {
if ( formElement.options[i].selected ) {
value.push( trim(formElement.options[i].value) );
}
}
} else {
value = trim( formElement.value );
}
this.validateValue( value, elem, rules, form, items );
};
// Validate Textarea or Input of type text, password,
// date, search, email etc
Validation.prototype.validateInput = function( elem, rules, formElement, form, items ) {
'use strict';
var value = '';
if ( formElement.hasAttribute('type') ) {
if ( formElement.getAttribute('type').toLowerCase() === 'password' ) {
value = formElement.value;
} else {
value = trim( formElement.value );
}
} else {
// Textarea value
value = trim( formElement.value );
}
this.validateValue( value, elem, rules, form, items );
};
// Validate Radio Buttons
Validation.prototype.validateRadio = function( elem, rules, nodeList, form, items ) {
'use strict';
var value = '';
for ( var i = 0, len = nodeList.length; i < len; i += 1 ) {
if ( nodeList[i].checked ) {
value = trim( nodeList[i].value );
break;
}
}
this.validateValue( value, elem, rules, form, items );
};
// Validate Checkboxes
Validation.prototype.validateCheckBox = function( elem, rules, nodeList, form, items ) {
'use strict';
var values = [];
for ( var i = 0, len = nodeList.length; i < len; i += 1 ) {
if ( nodeList[i].checked ) {
values.push( trim(nodeList[i].value) );
}
}
this.validateValue( values, elem, rules, form, items );
};
Validation.prototype.getMinMessage = function( name, minValue, expected ) {
'use strict';
expected = expected || '';
switch ( expected.toLowerCase() ) {
case 'integer':
return name + ' cannot be less than ' + minValue;
break;
case 'string':
return name + ' must be a minimum of ' + minValue + ' characters';
break;
default:
return 'You must select at least ' + minValue + ' ' + name;
break;
}
};
Validation.prototype.getMaxMessage = function( name, maxValue, expected ) {
'use strict';
expected = expected || '';
switch ( expected.toLowerCase() ) {
case 'integer':
return name + ' cannot be greater than ' + maxValue;
break;
case 'string':
return name + ' must be a maximum of ' + maxValue + ' characters';
break;
default:
return 'You must select a maximum of ' + maxValue + ' ' + name;
break;
}
};
// Check Minimum value
Validation.prototype.checkMin = function( value, minValue, expected ) {
'use strict';
expected = expected || '';
switch ( expected.toLowerCase() ) {
case 'integer':
return Number(value) < minValue;
break;
case 'string':
return String(value).length < minValue;
break;
default:
return value.length < minValue;
break;
}
};
// Check Maximum value
Validation.prototype.checkMax = function( value, maxValue, expected ) {
'use strict';
expected = expected || '';
switch ( expected.toLowerCase() ) {
case 'integer':
return Number(value) > maxValue;
break;
case 'string':
return String(value).length > maxValue;
break;
default:
return value.length > maxValue;
break;
}
};
// Check email value
Validation.prototype.checkDate = function( value ) {
'use strict';
var date = new Date( value );
return ( isNaN(date.getDate()) ) ? false : true;
};
// Check email value
Validation.prototype.checkEmail = function( value ) {
'use strict';
var emailRegex = /^[\w.%+\-]+@[\w.\-]+\.[A-za-z]{2,}$/;
return emailRegex.test( value );
};
Validation.prototype.getErrors = function() {
'use strict';
return this.errors;
};
Validation.prototype.getData = function() {
'use strict';
return this.data;
};
Validation.prototype.passed = function() {
'use strict';
return this.success;
};
function validateForm(e) {
'use strict';
var submitForm = false;
var formItems = {};
formItems['fullname'] = {
required: true, name: 'Full Name', min: 3,
max: 50, expected: 'string', pattern: /^([a-z ]|[^\d.\-])+$/i
};
formItems['date_of_birth'] = {
required: true, name: 'Date of Birth', expected: 'string', date: true
};
formItems['age'] = {
required: true, name: 'Age', min: 1,
max: 100, expected: 'integer', pattern: /^(-?[0-9]+)$/
};
formItems['height'] = {
required: true, name: 'Height', min: 1,
max: 12, expected: 'integer', pattern: /^(-?[0-9]+\.[0-9]+)$/
};
formItems['email'] = {
required: true, name: 'Email', email: true
};
formItems['password'] = {
required: true, name: 'Password', min: 8, expected: 'string'
};
formItems['rpt_password'] = {
required: true, name: 'Confirm Password', matches: 'password'
};
formItems['message'] = {
required: true, name: 'Message'
};
formItems['photo'] = {
required: true, name: 'Photo', permitted: [ 'jpg', 'png' ]
};
formItems['gender'] = {
required: true, name: 'Gender', expected: 'string'
};
formItems['color[]'] = {
required: true, name: 'Color(s)', min: 2
};
formItems['country'] = {
required: true, name: 'Country'
};
e = e || window.event;
var targetForm = e.target || e.srcElement;
var validation = new Validation( formItems );
validation = validation.check( targetForm );
if ( document.getElementById('list') ) {
var list = document.getElementById('list');
list.parentNode.removeChild( list );
var formElements = targetForm.getElementsByTagName('*');
formElements = [].slice.call(formElements);
formElements.forEach(function(val){
if ( !(val instanceof RadioNodeList) ) {
val.classList.remove('border-warning');
}
});
}
if ( validation.passed() ) {
var data = validation.getData();
console.log( JSON.stringify(data) );
} else {
var errors = validation.getErrors();
if ( !(isEmpty(errors)) ) {
var ul = document.createElement( 'ul' );
ul.setAttribute( 'id', 'list' );
ul.setAttribute( 'class', 'error' );
var li;
for ( var error in errors ) {
if ( {}.hasOwnProperty.call( errors, error ) ) {
li = document.createElement('li');
li.appendChild( document.createTextNode(errors[error]) );
ul.appendChild( li );
if ( !(targetForm[error] instanceof RadioNodeList) ) {
targetForm[error].classList.add('border-warning');
}
}
}
targetForm.parentNode.insertBefore( ul, targetForm );
}
}
// Prevent the form's submission:
if (e.preventDefault) {
e.preventDefault();
} else {
e.returnValue = false;
}
return submitForm;
}
addEvent( window, 'load', function(){
'use strict';
var form = document.getElementById( 'test-form' );
if ( !(form) ) { return; }
addEvent( form, 'submit', validateForm );
});
Output
You can jump to the latest bin by adding /latest
to your URL
Keyboard Shortcuts
Shortcut | Action |
---|---|
ctrl + [num] | Toggle nth panel |
ctrl + 0 | Close focused panel |
ctrl + enter | Re-render output. If console visible: run JS in console |
Ctrl + l | Clear the console |
ctrl + / | Toggle comment on selected lines |
ctrl + ] | Indents selected lines |
ctrl + [ | Unindents selected lines |
tab | Code complete & Emmet expand |
ctrl + shift + L | Beautify code in active panel |
ctrl + s | Save & lock current Bin from further changes |
ctrl + shift + s | Open the share options |
ctrl + y | Archive Bin |
Complete list of JS Bin shortcuts |
JS Bin URLs
URL | Action |
---|---|
/ | Show the full rendered output. This content will update in real time as it's updated from the /edit url. |
/edit | Edit the current bin |
/watch | Follow a Code Casting session |
/embed | Create an embeddable version of the bin |
/latest | Load the very latest bin (/latest goes in place of the revision) |
/[username]/last | View the last edited bin for this user |
/[username]/last/edit | Edit the last edited bin for this user |
/[username]/last/watch | Follow the Code Casting session for the latest bin for this user |
/quiet | Remove analytics and edit button from rendered output |
.js | Load only the JavaScript for a bin |
.css | Load only the CSS for a bin |
Except for username prefixed urls, the url may start with http://jsbin.com/abc and the url fragments can be added to the url to view it differently. |