Validator

for ie9 or above

all password input are replaced with text input for visual check

if you have any problem, let me know at kelly.kh.woo@gmail.com

default rules

reset methods resets dirty and touched as well

<label class="input-row">
  <span class="input-label">required
          <span class="sym-required"></span></span>
  <p><input type="text" class="ex0-a"></p>
</label>
<label class="input-row">
  <span class="input-label">email</span>
  <p><input type="text" class="ex0-b"></p>
</label>
<label class="input-row">
  <span class="input-label">password</span>
  <p><input type="text" class="ex0-c"></p>
</label>
<label class="input-row">
  <span class="input-label">minLen</span>
  <p><input type="text" class="ex0-d"></p>
</label>
<label class="input-row">
  <span class="input-label">maxLen</span>
  <p><input type="text" class="ex0-e"></p>
</label>
<label class="input-row">
  <span class="input-label">minNum</span>
  <p><input type="number" class="ex0-f"></p>
</label>
<label class="input-row">
  <span class="input-label">maxNum</span>
  <p><input type="number" class="ex0-g"></p>
</label>
          
var a = $vv.init('.ex0-a', {
  rules: [ 'required'],
  event: 'input'
})
var b = $vv.init('.ex0-b', {
  rules: [ 'email' ]
})
var c = $vv.init('.ex0-c', {
  rules: [ 'password' ]
})
var d = $vv.init('.ex0-d', {
  rules: [ {minLen: 10} ]
})
var e = $vv.init('.ex0-e', {
  rules: [ {maxLen: 5} ]
})
var f = $vv.init('.ex0-f', {
  rules: [ {minNum: 3000} ]
})
var g = $vv.init('.ex0-g', {
  rules: [ {maxNum: 50} ]
})
          

Cusome password Rule

<label class="input-row">
  <span class="input-label">custom password</span>
  <p><input type="text" class="ex1-a"></p>
</label>
<label class="input-row">
  <span class="input-label">confirm</span>
  <p><input type="text" class="ex1-b"></p>
</label>
          
$vv.addRule({
  pwHasLower: function (value) {
    return /[a-z]/.test(value)
  },
  pwHasUpper: function (value) {
    return /[A-Z]/.test(value)
  },
  pwHasDigit: function (value) {
    return /[0-9]/.test(value)
  },
  pwHasSpecial: function (value) {
    return /[@$!%*?&#+\-_]/.test(value)
  }
})
$vv.dictionary({
  pwHasLower: 'LowerCase',
  pwHasUpper: 'UpperCase',
  pwHasDigit: 'Digits',
  pwHasSpecial: 'Special Character(@$!%*?&#+-_)'
})
var a = $vv.init('.ex1-a', {
  rules: [ 'required', 'pwHasLower', 'pwHasUpper', 'pwHasDigit', 'pwHasSpecial', {
    minLen: 8,
    maxLen: 14
  } ],

  event: 'input',
  display: function (errors) {

    var str = '';
    if ( errors.length ) {
      var err = errors.reduce(function (p, c) {
        if ( /^pwH/.test(c[ 0 ]) ) {
          p[ 0 ].push(c)
        } else {
          p[ 1 ].push(c)
        }
        return p
      }, [ [], [] ]);
      str = err[ 0 ].length ? '

it needs ' + err[ 0 ].reduce(function (p, v) { p.push('' + v[ 1 ] + ''); return p; }, []).join(', ') + '

' : ''; str += err[ 1 ].map(function (v) { return '

' + v[ 1 ] + '

'; }).join(''); } this.showMessage(str); } }) var b = $vv.init('.ex1-b', { rules: [ { 'confirm': a } ] })

formatter

<label class="input-row">
  <span class="input-label">number(Float, Int) only</span>
  <p><input type="text" class="ex2-a"></p>
</label>
<label class="input-row">
  <span class="input-label">add comma after 3 digits (like cash)</span>
  <p><input type="text" class="ex2-b"></p>
</label>
          
var a = $vv.init(document.querySelector('.ex2-a'), {
  rules: [ 'required', { minNum: 200 } ],
  formatter: function (value) {
    return value.replace(/(^0(?!\.)|[^\d.])/g, '').replace(/^(\d+\.\d*)(\.)$/, '$1') || 0;
  }
})
var a = $vv.init(document.querySelector('.ex2-b'), {
  rules: [ 'required', { minNum: 1000 } ],
  formatter: function (value) {
    return value.replace(/\D|^0/g, '').replace(/\B(?=(\d{3})+(?!\d))/g, ',') || 0;
  }
})
          

refineValue

refineValue removes -, so minlength calculates only numbers when it validates

<label class="input-row">
  <span class="input-label">mobile number(maxLen:11)</span>
  <p><input type="text" class="ex4-a"></p>
</label>
<div style="text-align: center;padding: 30px 0 0">
  <button type="button" class="get-value-button btn-green">getValue</button>
</div>
          
var a = $vv.init(document.querySelector('.ex4-a'), {
  rules: [ 'numeric', {maxLen: 11}],
  formatter: function(value){
    return value.replace(/[^0-9]/g,'').replace(/\B(?=\d{4}\b)/,'-')
      .replace(/\B(?=\d{4}-\d{4}\b)/,'-')
  },
  refineValue:function(value){
   return value.replace(/-/g,'');
  }
});
var btn= document.querySelector('.get-value-button')
btn.addEventListener('click', function () {
 alert('the value returned from refineValue : '+a.getValue());
})
          

validateAll observeAll

radio
<label class="input-row">
  <span class="input-label">basic text<span class="sym-required"></span></span>
  <input type="text" class="a">
</label>
<dl class="input-row">
  <dt class="input-label">radio<span class="sym-required"></span></dt>
  <dd class="input-inline col3">
    <label class="input-row"><input type="radio" name="hey" class="b"><span
      class="input-label-inline">radio1</span></label>
    <label class="input-row"><input type="radio" name="hey" class="b1"><span
      class="input-label-inline">radio2</span></label>
    <label class="input-row"><input type="radio" name="hey" class="b2"><span
      class="input-label-inline">radio3</span>
    </label>
    <div id="radioMsg" class="msg-box"></div>
  </dd>
</dl>
<label class="input-row">
  <span class="input-label">select<span class="sym-required"></span></span>
  <select class="c">
    <option value="">default</option>
    <option value="hey">ddd</option>
  </select>
</label>
<label class="input-row">
  <span class="input-label">checkbox<span class="sym-required"></span></span>
  <input type="checkbox" class="d"><span class="input-label-inline">check please</span>
</label>
<div style="text-align: center;padding: 30px 0 0">
  <button type="button" class="validate-all btn-green">ValidateAll</button>
  <button type="button" disabled class="observe-all btn-green">ObserveAll</button>
</div>
          
var a = $vv.init('.a', {
  rules: [ 'required' ],
  formatter: function (value) {
    // return value;
  }
});
var b = $vv.init('.b', {
  rules: [ 'required' ],
  msgBox: document.querySelector('#radioMsg')
})
var c = $vv.init('.c', {
  rules: [ 'required' ],
  formatter: function (value) {
    return value;
  }
})
var d = $vv.init('.d', {
  rules: [ 'required' ]
})
//reactive validate

function suffice () {
  this.removeEventListener('click', suffice, false);
  this.classList.add('btn-processing');
  var $this = this
  setTimeout(function () {
    alert('Thank you for the submit');
    $vv.resetAll(a, b, c, d);
    $this.classList.remove('btn-processing')
  }, 2000)
}

var btnObserve = document.querySelector('.observe-all');

btnObserve.addEventListener('click',function () {
  b.reset();
  alert('unchecked radio button');
}, false);

$vv.observeAll(a, b, c, d, function (r, a, b, c, d) {
  if ( r ) {
    btnObserve.disabled = false;
  } else {
    btnObserve.disabled = true;
  }
})


//manual validate
var btnValidate = document.querySelector('.validate-all');
var isValidating = false;
btnValidate.addEventListener('click', validateForm, false)

function validateForm (e) {
  if ( isValidating ) {
    return;
  }
  var valid = $vv.validateAll(a, b, c, d);
  if ( valid.success ) {
    isValidating = true;
    btnValidate.classList.add('btn-processing');
    setTimeout(function () {
      alert('thank you for the submit');
      $vv.resetAll.bind(null, a, b, c, d);
      btnValidate.classList.remove('btn-processing');
      isValidating = false;
    }, 2000)
  } else {
    valid.failedFields[ 0 ].$el.focus();
  }
}
          

download

const $vv = require('path_to_$vv')
//$vv.init  => return Validator Instance
//$vv.addRule
//$vv.dictionary
//$vv.isValidAll
//$vv.validateAll
//$vv.observeAll
click to download

$vv.init

//$vv.init(element, options)

  let input = $vv.init('.a', {
    rules: [ 'required', 'password',
            {
              'confirm': validatorInstance,
              'minLen': 4
              //object can include parameters
            } ],
    msgBox: domObject,
    display: function(errors){
      // this refers to Validator Object
    },
    formatter: function(value) {
    },
    refineValue: function(value) {
    },
    event: 'change'
  });

init method returns Validator Instance

elementstring || element
object for input and select, or queryselector expression
optionsobject
event, applying rules, name, callbacks for new Validator Object
rulesstring || Object
the value of input, if a validator rule needs param, use Object and pass data
defined order matters.
(ex) rules: ['a', 'b', { c : dataObj, d : 'dataString' }]
msgBoxstring || element
where the error message to be displayed in, in case of string is provided, it computes with document.querySelector,
If it is not provided, Validator constructor will make a element for the error message and append it to the input's parent
displayfunction:void
set new display function for the instance.
'this' binds to Validator Object in the function.
since default one displays only first errors, in case editing error messages or do other stuff, it would be perfect.
argument errors has rule name and error message from dictionary and to display these to msgBox, call this.showMessages(errorMessage);
errors<Array(2),Array(2)...>
errors object
error[0]: rulename
error[1]: error message
[](empty array) for no errors
formatterfunction:void
change value and apply to the DOM Element, input events bound for 'texting' input only which means radio, checkbox, range, select can't set formatter.
it runs before validating rules and should return value.
ex) 1234 => 1,234
valuestring || number || boolean
presented dom value
number for type of range, number
boolean for checkbox, string for the reset
refineValuefunction:string
mutate the element's value for validating. like dom has value of '1,234' which is string, but validating value should be number of 1234, it can be helpful. If refine Value is set Validate method receives value returned from refineValue function. should return proper value.
eventstring
event to attach validate handler for text inputs (excludes radio, range, checkbox,select)
default is 'change' and default can be set by using config

$vv.addRule

var validateRule = function(value, args) {
 //this binds to Validator Object
  return boolean
}

//how to add rules
$vv.addRule(name, validateRule, errMessage)

//multiple
$vv.addRule({name1: validateRule1,
  name2: validateRule2,
  name3: validateRule3})

addRule adds global rules, is able to pass multiple rules as an object and in this case can't put messages together

namestring
the name of the rule
validateRulefunction: boolean
validate rule
valuestring||number||boolean
the value of input
range : number
checkbox : boolean checked
radio : string value of checked item
the rest are string
argsarray<any>
array made with arguments provided when Validate object was created except element and option
errMessagestring || function: string
error message can be string or function returns string
see below to Add Error Message

$vv.dictionary

var returnMessageFn = function(rulename, dictionary) {
  return 'error message to be displayed';
}
var returnMessageStr ='error message can be string data';
var errMessage = returnMessageFn || returnMessageStr;

//how to add error message
$vv.dictionary(name, errMessage)

//also multiple can be applied by passing object of messages
$vv.dictionary({name1: errMessage1,
            name2: errMessage2,
            name3: errMessage3})

dictionary adds global errorMessages and error message can be string or function returning string.
is able to pass multiple messages as an object

namestring
the name of the message(same as rules)
errMessagefunction: string || string
The error message to be shown when error occurred or function returns that error message

$vv.isValidAll

var a = $vv.init(...)
var b = $vv.init(...)
var btn = document.querySelector('button')

btn.addEventListener('click',function(){
  if($vv.isValidAll(a,b)) {
    ...
  }
}, false)

isValidAll doesn't provoke error messages
it simply returns the value in form of boolean

parametersValidator Instances
Validator instance to validate together
returnboolean
simply return true or false

$vv.validateAll

var a = $vv.init(...)
var b = $vv.init(...)
var btn = document.querySelector('button')

btn.addEventListener('click',function(){
  var valid = $vv.validateAll(a,b)

  if( valid.success) {
   ...
  } else {
   ...
  }
}, false)

it provokes all the error messages regardless its dirty or touched status

parametersValidator Instances
Validator instance to validate together
returnObject
success(boolean): all field has been validated
fail(boolean): some field hasn't been validated
fields(array): Validator Array passed in
failedFields(array): Validator object that failed(for success, it is undefined)

$vv.observeAll

var a = $vv.init(...)
var b = $vv.init(...)
var btn = document.querySelector('button')

$vv.observeAll(a,b, function(valid) {
  if(valid) {
    btn.classList.add('btn-red')
  } else {
    btn.classList.remove('btn-red')
  }
})

observeAll does reactive work to its validator instances' value
take one function to call when value changes,
can be used for UI mutation

parametersValidator Instances
Validator instance to validate together
returnboolean
return true for all valid, false for other cases

$vv.config

//default config object
default = {
    boxClass: 'msg-box', // msg-box class, no padding, no styling should be applied
    msgClass: 'msg-box-msg', // div inside msg-box
    errorClass: 'is-error',
    interval: 500,
    waiver: 'dirty',
    event: 'change',
    //_config.countBlank:false => uncount errors(except required) when string is ''
    getDefaultMsg: function (rule) {
      return rule + ' : entered wrong'
    },
    display: function (errors) {
    // override function to display error message

    }
  }
 $vv.config({event:'input',countBlank:true....})
boxClassstring
className for wrapper of message box
msgClassstring
className for the Validator.$msgBox
errorClassstring
className for the Validator.$el when error occurred
intervalnumber
validation throttle time, default is 500
waiverstring
option is 'dirty' or 'touched'
'dirty' for not showing error message before key(text input) or mousedown(not text input) event
'touched' for focus event
eventstring
change validate event for all Validator to be created
displayfunction
change display function for all validator to be created
errorsArray
errors object
error[0]: rulename
error[1]: error message
[](empty array) for no errors

Validator constructor

Properties

Validator.$elelement
Element object to be validated(only input and select)
Validator.$msgBoxelement
Element object to display error message
Validator.dirtyboolean
mousedown for checkbox, radio, range, input for the rest
true for the case event has been occurred and false for the other
Validator.touchedboolean
focus
true for the case event has been occurred and false for the other
Validator.pureboolean
opposite of dirty
Validator.untouchedboolean
opposite of touched
Validator.failedboolean
error happened
Validator.successboolean
passed all rules

Methods

Validator.setValuefunction:void
set Value to the Element and run the validate method
valueany
value to change
Validator.getValueany
value of form element returned from refineValue It can be differed from element's value by what refineValue function returns;
Validator.addRulefunction:void
add a rulename to apply to Validator instance
rulenamestring
rule to add to the instance(not recommend to use)
Validator.removeRulefunction:void
remove a rulename to apply to Validator instance
rulenamestring
rule to remove from the instance(not recommend to use)
Validator.showMessagefunction:void
receive string(error message) and display it to msgbox
messagestring
message to display at error box
Validator.showMsgBoxfunction:void
display block to Validator.$msgBox
Validator.hideMsgBoxfunction:void
display none to Validator.$msgBox
Validator.removefunction:void
remove Validator instance and eventlisteners