只需引入模塊無需額外初始化,便可在onchange時對全部綁定data-rule="規則"
屬性的表單元素觸發校驗。css
除內置的規則(url、email、整數和浮點數等),還容許用戶註冊新的「規則」(見register方法)。html
容許用戶重置「錯誤信息的展現、errorHander、successHander」等行爲(經過reset接口見下文),這樣設計是爲了更靈活的與第三方css組件融合jquery
在submit時,只需手動調用一個方法,並給該方法指定一個「範圍」,便可獲得校驗結果(範圍內的全部「輸入」是否全都有效)git
總之,要校驗誰就給誰加data-rule,再簡易的配置一下規則便可。es6
<input type="text" name="link" placeholder="連接" data-rule="url" data-help="不能爲空" required>
import form from './form.js' import $ from 'jquery';
初始化完畢~,以上input在value改變時便可觸發內置的url
格式校驗。github
必選項,只有包含data-rule屬性的表單元素纔會被校驗。支持兩種寫法:正則表達式
data-rule="類型"函數
data-rule="類型|表達式"ui
關於第一種寫法,全部內置類型(共7種,見下表)都支持這麼寫。
關於第二種寫法,只有「zh,en,number,select」3種類型才支持這種寫法。表達式中容許出現3個屬性:size、decimal、type(後2個只能在number類型的表達式中出現)this
<input type="text" name="nickname" data-rule="zh|size:6-8"> <input type="text" name="username" data-rule="eh|size:6-8"> <input type="text" name="total" data-rule="number|size:6-8,decimal:2-4,type:-1" >
szie 後面的值容許使用'-'表示範圍(6到8位中文),固然更能夠只寫一個單純的數字
decimal 是number類型的專屬,表示保留幾位小數,也能夠不寫,表示不限制小數
type 是number類型的專屬,值只能是'+1或-1',分別表示正負數。也能夠不寫,
全部類型:
類型 | 說明 |
---|---|
url | 連接,帶不帶協議頭都可,支持ftp(s)和http(s) |
regexp | 正則表達式,把值寫在pattern屬性上,模塊會取這裏的值進行校驗 |
REG_* | 經過register方法添加自定義的類型 |
郵箱 | |
zh | 中文,支持寫法2 |
en | 英文,支持寫法2 |
number | 數字,支持寫法2,正負數、整數和浮點數 |
特別說明:類型爲regexp時,正則表達式須要寫在pattern屬性上。
<input type="text" name="正則校驗" data-rule="regexp" pattern="^\d{1,3}$">
選填項,錯誤時error wording從這裏取
選填項,表示該input
爲必填項,不寫則容許空值。好比下例容許空值,但非空時會校驗輸入值是否有效:
<input type="text" name="link" placeholder="連接" data-rule="url" data-help="不能爲空">
註冊新的數據規則
import form from './form.js' import $ from 'jquery'; //添加兩種數據格式 form.register({ REG_PASSWORD: /^[A-Z]\w{5,11}$/, REG_NUMBER: /^\d+\.\d{2}$/ })
type
字符串類型,容許出現warn、errorHandler和successHander。
warn表示重置錯誤信息的展現方式,默認是以alert方式,能夠傳入第三方的toptip或者toast插件。
form.reset('warn', $.toptip) //或者 var toast = function(type) { return function(msg) { $.toast(msg, type) } } form.reset('warn', toast('forbidden'))
errorHander 表示校驗返回失敗時執行的動做,模塊內會默認給重置的slot函數傳入$input對象
form.reset('errorHandler', ($input)=>{ $input.closest('.form-group').addclass('has-error') })
以上實現了校驗失敗時,往第一個臨近當前input的.form-group元素上添加'has-error'樣式。 若是不重置,默認是添加在input上
successHandler 表示校驗返回成功時執行的動做。用法與errorHandler相同。
注意:errorHandler和successHandler的重置是對稱的,不能只重置一個。由於若是隻重置errorHanlder(把.has-error掛到.form-group上),success時,默認是從input上移除.has-error的
slot
函數類型
selector
字符串類型,符合jquery語法的選擇器。校驗指定選擇器內的全部表單元素的輸入是否有效,返回true或false
import $ from 'jquery'; /** * * 用法設計 * * ```html * <input type="text" name="link" placeholder="連接" data-rule="url" data-help="不能爲空" required> * ``` * * * @data-rule 表示該input應輸入的值類型,有兩種寫法 * 1. data-rule="類型" * - url * - email * - zh * - en * - regexp 當設置該類型時,必須跟上pattern屬性,值爲正則表達式 * - REG_* 經過register方法註冊的自定義類型 * * 2. data-rule="類型|表達式" * 表達式只會出現3種屬性:size、decimal、type(後二者只用於描述number類型) * - "en|size:3" 表示只能輸入3位英文 * - "zh|size:3-10" 表示容許輸入3到10位的中文 * - "number|size:3-10,type:'+1',decimal:2" * + type 表示正數(+1),負數(-1),沒有type表示正負數以及0 * + decimal 表示小數點保留幾位,沒有decimal表示整數 * + size 表示位數,沒有表示不限位數 * - "select|size:1-3" 表示容許選中幾個,通常用於checkbox的第一個 * * @data-help 報錯的wording會從 data-help | placeholder 裏得到 * * * @required 選填屬性。不加該屬性表示該項容許空值(即非必填) */ let defaults = { //支持完整(http://www.a.com)的或者不帶協議(www.a.com)的url //而且協議只認http(s)和ftp(s) url: /^((f|ht){1}(tp|tps):\/\/)?([\w-]+\.)+[\w-]+(\/[\w- .\?%&=]*)?/, //@左邊只容許數字、字母、'.'和'_' //@右邊容許出現@sina.com.cn這樣的格式 email: /^(\w)+(\.\w+)*@(\w)+((\.\w+)+)$/, //純中文 zh: /^[\u4e00-\u9fa5]+$/, //純英文 en: /^[a-zA-Z]+$/, //純數字,支持正負數,浮點數,以及"1,300,268"這樣的寫法 number: /^-?\d+(\.\d+)?$/, //不包含`,~,!,@,#,$,%,^,&,*,(,),+,<,>,?,:,",{,},\,/,;,',[,]的任意字符 // word: /[`~!@#\$%\^\&\*\(\)\+<>\?:"\{\}\\\/;'\[\]]/im, regexp: '', select: '' }; let successClass = 'has-success'; let errorClass = 'has-error'; let patterns = defaults; /** * 展現錯誤信息 * @param {String} msg [錯誤信息] * @param {Function} theWay [默認以alert方式,能夠經過暴露的reset接口重置] * @return {[type]} [展現錯誤信息] */ let _warn = function(msg, say = alert) { say(msg) }; let _errorHandler = function($input) { $input.addClass(errorClass).removeClass(successClass); }; let _successHandler = function($input) { $input.removeClass(errorClass).addClass(successClass); }; /** * 校驗input * @param {Object} $input [jq對象] * @return {Boolean} [true校驗經過,false校驗失敗] */ let _checkIt = function($input) { //一、'data-rule'值爲空,不校驗 //二、'data-rule'值爲'regxp',但'pattern'爲空,不校驗 //三、 必填項爲空值時,直接提示(再也不進行parseRule) //四、非比填項容許爲空值 //五、data-rule和input value都不爲空時,無論有沒required都得校驗 if (!$input.data('rule') || ($input.data('rule') == 'regexp' && !$input.prop('pattern'))) { return false } else if (!$input.val() && $input.prop('required')) { return false } else if (!$input.val() && !$input.prop('required')) { return true } else if ($input.val()) { let regxp = _parseRule($input); return regxp.test($input.val()) ? true : false; } }; /** * 解析data-rule * @param {Object} $input [jq對象] * @return {Object} [返回一個正則表達式對象(若是是'select|3-10'類型的會返回位數)] */ let _parseRule = function($input) { let rule = $input.data('rule').trim().toLowerCase(); let type = rule.match(/^(\w+)(\|[\w|\W]+)?/)[1]; let pattern; //判斷type是否存在 》屬於哪一種rule類型 》對第二種類型進一步分類解析 if (!patterns.hasOwnProperty(type)) { throw type + ' is not registered' } else if (/^\w+$/.test(rule)) { if (type == 'regexp') { pattern = new RegExp($input.prop('pattern')); } else { pattern = patterns[type]; } } else if (/^\w+\|[\w|\W]+/.test(rule)) { // 'size:1-10,decimal:1-2,type:+1' let exp = rule.slice(type.length + 1); //須要扣除掉'|'' let obj = {}; exp.split(',').forEach((item) => { obj[item.split(':')[0]] = item.split(':')[1] }) if (type == 'number') { if (obj['size'] && !obj.hasOwnProperty('decimal') && !obj.hasOwnProperty('type')) { // /^-?\d{1,3}$/ 限定位數的整數 pattern = new RegExp('^-?\\d{' + obj['size'].replace('-', ',') + '}$'); } else if (obj['size'] && obj['type'] == '+1' && !obj.hasOwnProperty('decimal')) { // /^\d{1,3}$/ 限定位數的正整數 pattern = new RegExp('^\\d{' + obj['size'].replace('-', ',') + '}$'); } else if (obj['size'] && obj['type'] == '-1' && !obj.hasOwnProperty('decimal')) { // /^-\d{1,3}$/ 限定位數的負整數 pattern = new RegExp('^-\\d{' + obj['size'].replace('-', ',') + '}$'); } else if (obj['type'] == '+1' && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('decimal')) { // /^\d+$/ 不限定位數的正整數 pattern = new RegExp('^\\d+$'); } else if (obj['type'] == '-1' && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('decimal')) { // /^-\d+$/ 不限定位數的負整數 pattern = new RegExp('^-\\d+$'); } else if (obj['decimal'] && !obj.hasOwnProperty('size') && !obj.hasOwnProperty('type')) { // /^-?\d+\.\d{1,3}$/ 浮點數(正負都可),只定小數點的 pattern = new RegExp('^-?\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (!obj.hasOwnProperty('size') && obj['type'] == '+1' && obj['decimal']) { // /^\d+\.\d{1,3}$/ 正浮點數,只定小數點的 pattern = new RegExp('^\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (!obj.hasOwnProperty('size') && obj['type'] == '-1' && obj['decimal']) { // /^-\d+\.\d{1,3}$/ 負浮點數,只定小數點的 pattern = new RegExp('^-\\d+\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (obj['size'] && !obj.hasOwnProperty('type') && obj['decimal']) { // /^-?\d+\.\d{1,3}$/ 浮點數(正負都可),整數部分和小數部分均限定 pattern = new RegExp('^-?\\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (obj['size'] && obj['type'] == '+1' && obj['decimal']) { // /^\d{1,3}\.\d{1,3}$/ 正浮點數,整數部分和小數部分均限定 pattern = new RegExp('^\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } else if (obj['size'] && obj['type'] == '-1' && obj['decimal']) { // /^-\d{1,3}\.\d{1,3}$/ 負浮點數,整數部分和小數部分均限定 pattern = new RegExp('^-\\d{' + obj['size'].replace('-', ',') + '}\\.\\d{' + obj['decimal'].replace('-', ',') + '}$'); } } else if (type == 'zh') { //砍掉尾巴2位('+$''),而後拼接{form,to} pattern = new RegExp('^[\\u4e00-\\u9fa5]{' + obj['size'].replace('-', ',') + '}$'); } else if (type == 'en') { pattern = new RegExp('^[a-zA-Z]{' + obj['size'].replace('-', ',') + '}$') } else if (type == 'select') { pattern = obj['size']; } } else { throw rule + ' is wrong.'; } return pattern }; // blur校驗input value $(document).on('change', ':input[data-rule]', function(event) { if ($(this).type == 'checkbox' || $(this).type == 'radio') return if (_checkIt($(this))) { _successHandler($(this)); } else { _warn($(this).data('help') || $(this).prop('placeholder') || '格式錯誤'); _errorHandler($(this)); } }) export default { /** * 校驗指定範圍內的全部表單元素的輸入是否有效 * @param {String} selector [description] * @return {Boolean} [返回true或false] */ validate(selector) { let isValid; $(selector).find(':input[data-rule]').each(function() { if (_checkIt($(this))) { _successHandler($(this)); isValid = true; } else { _warn($(this).data('help') || $(this).prop('placeholder') || '格式錯誤'); _errorHandler($(this)); isValid = false; return false } }) return isValid; }, /** * 註冊一個新的校驗類型 * @param {Object} object {type:'REG_NAME',pattern:正則表達式} * @return {[type]} 往默認的defaults對象裏添加新的鍵值對 */ register(object) { $.each(object, function(key, val) { if (!/^REG_/i.test(key)) { throw object.type + ' is invalid name.it must start with "REG_"' } //默認屬性放後頭,確保不會被新註冊的覆蓋 $.extend(patterns, { [key.toLowerCase()]: val }, defaults); }) }, /** * 暴露出來的重置接口 * @param {String} type ['warn|errorHandler|successHandler',warn表示展現錯誤信息;errorHander表示錯誤時執行的操做] * @param {Function} slot [函數或字符串] * @return [重置內部的方法] */ reset(type, slot) { if (type == 'warn') { _warn = slot; } else if (type == 'errorHandler') { _errorHandler = slot; } else if (type == 'successHandler') { _successHandler = slot; } else { throw type + ' is not invalid type' } } }