具備哪些能力
-
只需引入模組無需額外初始化,即可在onchange時對所有繫結
data-rule="規則"
屬性的表單元素觸發校驗。 -
除內建的規則(url、email、整數和浮點數等),還允許使用者註冊新的“規則”(見register方法)。
-
允許使用者重置“錯誤資訊的展示、errorHander、successHander”等行為(通過reset介面見下文),這樣設計是為了更靈活的與第三方css元件融合
-
在submit時,只需手動呼叫一個方法,並給該方法指定一個“範圍”,即可得到校驗結果(範圍內的所有“輸入”是否全都有效)
總之,要校驗誰就給誰加data-rule,再簡易的配置一下規則即可。
安裝
<input type="text" name="link" placeholder="連結" data-rule="url" data-help="不能為空" required>
import form from `./form.js`
import $ from `jquery`;
初始化完畢~,以上input在value改變時即可觸發內建的url
格式校驗。
屬性
@data-rule *
必選項,只有包含data-rule屬性的表單元素才會被校驗。支援兩種寫法:
-
data-rule=”型別”
-
data-rule=”型別|表示式”
關於第一種寫法,所有內建型別(共7種,見下表)都支援這麼寫。
關於第二種寫法,只有“zh,en,number,select”3種型別才支援這種寫法。表示式中允許出現3個屬性:size、decimal、type(後2個只能在number型別的表示式中出現)
<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}$">
@data-help
選填項,錯誤時error wording從這裡取
@required
選填項,表示該input
為必填項,不寫則允許空值。比如下例允許空值,但非空時會校驗輸入值是否有效:
<input type="text" name="link" placeholder="連結" data-rule="url" data-help="不能為空">
方法
@regeister(object)
註冊新的資料規則
import form from `./form.js`
import $ from `jquery`;
//新增兩種資料格式
form.register({
REG_PASSWORD: /^[A-Z]w{5,11}$/,
REG_NUMBER: /^d+.d{2}$/
})
@reset(type,slot)
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
函式型別
@validate(selector)
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) {
//1、`data-rule`值為空,不校驗
//2、`data-rule`值為`regxp`,但`pattern`為空,不校驗
//3、 必填項為空值時,直接提示(不再進行parseRule)
//4、非比填項允許為空值
//5、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`
}
}
}