I guess I am not sure what the validateInput()
function is intending to do if the validation happens before you even call that code.
The essence of your problem is that you need to handle different events on an arbitrary set of fields that need to trigger a validation event.
That means, all you really should need to do from an event binding point of view is something like this
$('.target_element_selector').on('keyup change focusout', function(){
validateInput(this);
});
Or for delegated binding:
$('.common_ancestor_selector').on(
'keyup change focusout',
'.target_element_selector',
function(){
validateInput(this);
}
);
this
in within this function context will have everything you need to know to perform a validation. From it you can reference the current value for the field, the type of validation rule that should be applied, and of course the element itself should you need to modify it (not discussed here).
Inside your validation method is where you should hold the logic on how validations occur, not outside of it, otherwise, why have the function at all?
Now for the validation method...
At it's simplest this method needs to: determine input value, determine validation to apply, and return validation result.
That could look something like this:
function validateInput(el) {
var $el = $(el);
// My suggestion would be to set data properties on element
// rather than classes to indicate validation enforcement.
// This is easier than iterating through classes.
var rule = $el.data('validation-rule');
if(rule === 'undefined') {
// there is no validation rule, so let's pass validation
return true;
}
if(rule in validationFunctions){
return validationFunctions[rule]( $el.val() );
}
throw new Error('Unknown validation rule "' + rule + '" specified.');
};
Which would work with a set of validation functions like this (note I am using arrow functions here which seem to lend brevity to this code).
// set of validation functions
var validationFunctions = {
'not-null':
value => { return value.length > 0; },
'two-words':
value => { return value.match(/^[A-Za-zÀ-ú]+ [A-Za-zÀ-ú]+/i); },
'numb':
value => { return value.match(/^(?=.*\d)\d*[\.\,]?\d*$/); },
// etc.
};
Elements could look like:
<input class="js-mandatory" data-validation-rule="not-null" ...>
So it is really easy for someone writing the front end to simply specify a given
validation rule and have the code automatically add this field to the set of fields being validated.
Note that this doesn't show how to change the display around the element, but that wasn't part of you original code, so I didn't expound on that area.
Added
I just noticed in working on code example below that you are using string.match()
when it might be better to use /{regex}/.test()
to be able to return boolean (probably good to enforce this as requirement for all test validation functions). This will likely perform better as well.
Since you have clarified your use case, let me give a more practical example than what I gave above. Here we build an inputElementValidator
object that could be used on any element that supports $.val()
and allows you to pass a callback to the validation function which it can call with success/failure. This allows you to cleanly decouple your validation mechanism, making it potentially re-usable across multiple applications.
On your page itself, you simply instantiate your validator, define the callback you want to trigger in response to validation, and then attach event handler to call validation.
/*
* Here is your class which could be included from separate file
*/
function inputElementValidator(validators) {
this.validators = $.extend(this.defaultValidators, validators);
};
// class method(s)
inputElementValidator.prototype = {
validate: function (el, callback) {
var $el = $(el);
// get validation to be applied from element attribute
var rule = $el.data('validation-rule');
if(rule === 'undefined') {
// there is no validation rule, so let's pass validation
return callback(el, true);
}
// validator must return boolean
if(rule in this.validators){
// callback must support two arguments - the element and validation status
return callback(el, this.validators[rule]);
}
throw new Error(
'Unknown validation rule "' + rule + '" specified.'
);
}
};
// class "static" property
inputElementValidator.defaultValidators: {
'not-null':
value => { return value.length > 0; },
'two-words':
value => { return /^[A-Za-zÀ-ú]+ [A-Za-zÀ-ú]/.test(value); },
'numb':
value => { return /^(?=.*\d)\d*[\.\,]?\d*$/.test(value); },
// etc.
};
/*
* Here is your code in your page
*/
// perhaps wrap in IIFE to make this self-contained scope
(function() {
// first, set up validator with whatever rules above and beyond default
var inputValidator = new inputElementValidator({
'new-or-overridden-validation-rule':
value => { return /* something */; }
});
// create your validation callback function
// this is where you apply your logic to display validation success/failure
function validationCallback(el, success) {
if(success) {
// do something with el or $(el)
} else {
// do something else
}
}
// attach event handling
$('.common_ancestor_selector').on(
'keyup change focusout',
'.target_element_selector',
function() { inputValidator.validate(this, validationCallback); }
);
})();