Skip to content

Instantly share code, notes, and snippets.

@fczbkk
Created May 22, 2017 06:49
Show Gist options
  • Select an option

  • Save fczbkk/ef9639c82ec90ecb47b6b0e57f9242eb to your computer and use it in GitHub Desktop.

Select an option

Save fczbkk/ef9639c82ec90ecb47b6b0e57f9242eb to your computer and use it in GitHub Desktop.
Simple extensible form validation
<html>
<head>
<script>
/**
* @typedef {Object} validation_result
* @property {('ok'|'fail')} status
* @property {string} [message]
*/
/**
* @typedef {Object} field_config
* @property {Element} field
* @property {string} validator - Validator identifier
*/
/**
* @typedef {Object} error_data
* @property {Element} field
* @property {string} message
*/
var validators = {
/**
* Checks for any non-whitespace character.
* @param {string} input
* @returns {validation_result}
*/
is_not_empty: function (input) {
return (/\S/g.test(input))
? {status: 'ok'}
: {status: 'fail', message: 'Field should not be empty.'};
},
/**
* Checks for any non-number character.
* @param {string} input
* @returns {validation_result}
*/
is_numeric: function (input) {
return (/\D/g.test(input))
? {status: 'fail', message: 'Field should only contain numbers.'}
: {status: 'ok'};
}
};
/**
* Routes field config to appropriate validator function.
* @param {field_config} field_config
* @returns {validation_result}
*/
function checkField (field_config) {
return validators[field_config.validator](field_config.field.value);
}
/**
* Helper to be used via Array.reduce to compile all errors produced during form validation.
* @param {Array.<validation_result>} existing_errors
* @param {field_config} field_config
* @returns {Array.<validation_result>}
*/
function validateField (existing_errors, field_config) {
var result = checkField(field_config);
if (result.status !== 'ok') {
existing_errors.push({
message: result.message,
field: field_config.field
});
}
return existing_errors;
}
/**
* Routes error data to reporter.
* @param {Function} reporter
* @param {error_data} error_data
*/
function reportError (reporter, error_data) {
reporter(
'Validation error:', error_data.message,
'\nin field:', error_data.field
);
}
/**
* Creates field validator. Returns object with `destroy()` method for easier cleanup.
* @param {Element} form_element
* @param {Array.<field_config>} fields_config
* @param {Function} [reporter=console.log]
* @returns {{destroy: destroy}}
*/
function createFormValidator (form_element, fields_config, reporter) {
reporter = reporter || console.log;
var handleFormSubmit = function (event) {
var validation_errors = fields_config.reduce(validateField, []);
if (validation_errors.length > 0) {
validation_errors.forEach(function (error_data) {
reportError(reporter, error_data);
});
event.preventDefault();
}
};
form_element.addEventListener('submit', handleFormSubmit);
return {
destroy: function () {
form_element.removeEventListener('submit', handleFormSubmit);
}
}
}
function init () {
createFormValidator(
document.getElementById('my_form'),
[
{
field: document.getElementById('non_empty_input'),
validator: 'is_not_empty'
},
{
field: document.getElementById('numeric_input'),
validator: 'is_numeric'
}
]
);
}
window.addEventListener('load', init);
</script>
</head>
<body>
<ul>
<li>If there are no errors, form will submit.</li>
<li>If there are any errors, form submission will be cancelled and error messages will appear in console.</li>
</ul>
<form id="my_form">
<p>
<label for="non_empty_input">Must not be empty</label>
<input type="text" id="non_empty_input">
</p>
<p>
<label for="numeric_input">Must only contain numbers</label>
<input type="text" id="numeric_input">
</p>
<p>
<input type="submit">
</p>
</form>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment