安装与调用
引用下面CSS:
<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/hope/ui/Validate/index.css">
JS为:
<script type="module" src="https://qidian.gtimg.com/lulu/hope/ui/Validate/index.js"></script>
或者import导入:
<script>
import Validate from 'https://qidian.gtimg.com/lulu/hope/ui/Validate/index.js';
</script>
基础功能演示
基于HTML5规范的表单验证交互组件。依赖 ErrorTip 组件。
- 内置type类型包括:
email,tel,url,zipcode,number,date,hour,minute,time。 - 自定义正则表达式,使用原生 HTML 属性
pattern,例如:pattern="^\d{16}$"。 - 范围超出,如数值输入,数值范围选择,以及日期和时间的起止限制,使用原生
min,max,step属性。 - 内置计数和计数验证功能,使用原生
maxlength, 以及minlength。内容长度超出提示的时候会自动选择超出部分的文字。 - 支持指定提示信息和自定义验证方法,具体参见这里。
测试兼演示
下面表单采用组件默认验证交互,即点击“提交”按钮后开启即时验证,所有有误内容标红,但是出错提示只会出现在第一个或当前激活的控件上。
// 表单验证
var elePwd = document.querySelector('#inputPwd');
var elePwdAgain = document.querySelector('#inputPwdAgain');
// 文件上传按钮元素
var eleBtnFile = document.querySelector('label[for="file"]');
// 隐藏input框
var eleHiddenFile = document.querySelector('#inputImgUrl');
// 如果选择文件
file.addEventListener('change', function () {
eleHiddenFile.value = 'xxx.png';
// 触发change事件执行
eleHiddenFile.dispatchEvent(new CustomEvent('change'));
});
// 验证绑定
new Validate('#validateForm', function () {
new Dialog().alert('全部验证通过');
}, {
// 自定义验证提示和方法
validate: [{
id: 'inputBank',
report: {
// 改变内置的提示信息为自定义
patternMismatch: '银行账户只能是16位数值'
}
}, {
id: 'inputPwd',
method: function (ele) {
var valueAgain = elePwdAgain.value;
if (valueAgain && ele.value != valueAgain) {
return '前后密码不一致';
}
// 再次输入密码样式变化,后一个参数必需
this.styleError(elePwdAgain, !!elePwdAgain.value);
}
}, {
id: 'inputPwdAgain',
method: function (ele) {
if (ele.value != elePwd.value) {
return '前后密码不一致';
}
// 输入密码样式变化,后一个参数必需
// 否则会和上面验证来回执行导致死循环
this.styleError(elePwd, !!elePwd.value);
}
}, {
id: 'inputImgUrl',
method: function (ele) {
// ele指存放身份证图片地址的hidden类型的input框
// 隐藏元素无法定位,转移提示元素给按钮
ele.dataTarget = eleBtnFile;
if (ele.value == '') {
return '尚未上传证件照';
}
}
}]
});
证件照验证相关HTML代码:
<label for="file" class="ui-button">上传照片</label> <input type="hidden" id="inputImgUrl"> <!-- 上传文件的表单 --> <form><input type="file" id="file" accept="image/*"></form>
注意,上面 hidden 类型的 <input> 元素自定义的 method 方法实现功能是为空验证,该功能也可以通过添加 required 属性实现,如下:
<input type="hidden" id="inputImgUrl" required>
然后使用report参数改变提示内容。例如:
// required设置时候的处理
{
id: 'inputImgUrl',
report: {
// 指定值缺失时候的提示文字
valueMissing: '尚未上传头像'
}
}
语法和参数
语法
new Validate(element, callback, options);
参数
其中:
- element
- 必须。Element|String。可以是 DOM 元素,也可以是 DOM 元素的选择器。通常指
<form>元素。 - callback
- 可选。Function。全部验证通过之后的回调方法。其中上下文
this指当前实例对象,支持一个参数,表示element参数对应的元素。 - options
- 可选。Object。可选参数,具体见下表:
| 参数名称 | 支持类型 | 默认值 | 释义 |
|---|---|---|---|
| validate | Array Object |
[] | 自定义提示和验证方法。为对象数组集。如 [{}, {}]。如果仅有一个数组项,也可以直接使用该数组项作为参数。
本参数要点较多,本文档有专门深入介绍,详见这里。 |
| multiple | Boolean | true | 提交时候是全部错误高亮,还是仅仅第一个。 |
| immediate | Boolean | true | 是否在表单提交后开启即时验证。 |
| label | Boolean | true | 是否使用关联 <label> 元素中裸露的文字作为提示关键字,会自动过滤冒号,此参数非常实用。 |
事件
参与表单验证的所有控件元素均支持原生的 valid 和 invalid 事件,分别在验证通过和验证不通过的时候触发(本文档页面对底部案例有使用示意)。
另外,当所有表单控件元素都验证通过的时候,会触发表单元素自定义的 valid事件,例如:
form.addEventListener('valid', function () {
// 表单验证通过
});
validate可选参数深入
validate 参数实际使用多这样:
new Validate('formId', {
validate: [{
id: 'inputId',
report: {
/* 设置错误类型对应提示 */
},
method: function () {
/* 额外自定义验证方法,返回customError提示文字 */
}
}]
});
如果 validate 参数值的数组项只有一个,可以直接使用该数组项作为参数,示意:
new Validate('formId', {
validate: {
id: 'inputId',
report: {},
method: function () {}
}
});
数组对象深入
数组项为纯对象,支持下面几个参数:
- id
- 必需。String。目标验证输入框或选择框元素的
id属性值。 - report
- 可选。Object。自定义出错的提示文字。支持以下提示字段:
-
{ badInput: '提示文字...' customError: '...' patternMismatch: '...' rangeOverflow: '...' rangeUnderflow: '...' stepMismatch: '...' tooLong: '...' tooShort: '...' typeMismatch: '...' valueMissing: '...' }例如
valueMissing的值缺失。所有这些提示字段源自浏览器原生验证的 ValidityState 对象。每个字段对应的含义可以参见这篇文章:https://www.zhangxinxu.com/wordpress/?p=8895所有提示字段值还支持 Function 类型,用在需要动态返回提示内容的场景。
- method
- 可选。Function。自定义验证方法。
其中上下文 this 指当前实例对象,支持一个参数,为当前验证元素。
返回错误提示内容字符串,或
{ customError: '...' }对象。其他返回值(包括
undefined)都认为是验证通过。
自定义提示案例
下面有一个普普通通的输入框和一个普普通通的按钮:
<input class="ui-input" required> <button is="ui-button" type="primary">点我</button>
点击按钮,会触发验证提示,这里直接使用原生语法示意,如下:
button.addEventListener('click', function () {
input.reportValidity();
});
如果输入框里面没有值,此时出现的提示文案是内置的“请填写此字段”。
如果我们想要改变出现的提示文案,可以有下面两种方法:
- 借助Validate实例方法,我们可以随便找个输入框的祖先元素当作表单元素(实际开发外面需要有
<form>元素,这里示意省略了)。new Validate(input.parentElement, { validate: { id: 'input', report: { valueMissing: '主人,这里还没有输入内容,喵~' } } }); button.addEventListener('click', function () { input.reportValidity(); });效果如下:
- 使用原生的 API 语法
Element.setCustomValidity(),如下:input.setCustomValidity({ valueMissing: '主人,请输入内容,喵呜~' }); button.addEventListener('click', function () { input.reportValidity(); });效果如下:
然而,实际开发,其实使用更多的是借助 <label> 元素重置宽泛的提示。详见这里。
自定义验证方法案例
演示 method 参数以及隐藏元素的提示功能实现,此参数的基本用法其实在一开始的“测试与演示”案例那里有演示,这里再细化下。
首先演示下基本功能:
下面有一个普普通通的输入框和一个普普通通的按钮:
<input class="ui-input"> <button is="ui-button" type="primary">点我</button>
此时,我们观察
<input>输入框对应的 HTML 代码,可以看到没有设置required属性,默认状态下,认为是合法的。如果我们希望此时内容为空也提示错误,而 HTML 又没法设置,怎么办呢?可以使用
method参数,JS 代码如下:new Validate(input.parentElement, { validate: { id: 'input', method: function (element) { if (element.value.trim() == '') { return '这个是必填的哟~'; } } } }); button.addEventListener('click', function () { input.reportValidity(); });在本例中,为了演示,所以外面没有
<form>元素,在这种场景下,想要自定义验证方法,还有更简单的实现。所有的自定义提示和方法都是使用Element.customValidity实现的,因此,上面代码我们还可以简化成这样:input.customValidity = { method: function (element) { if (element.value.trim() == '') { return '这个是必填的哟~'; } } }; button.addEventListener('click', function () { input.reportValidity(); });- 然后演示下隐藏元素的提示,有些表单控件是隐藏在页面中的,例如
'hidden'类型输入框,或者一些很丑的拿不出台面的元素。Validate 组件对 LuLu UI 内置的表单元素已经做了提示元素转移,但是并不能覆盖所有的场景,此时,需要开发人员针对具体的场景进行调整。有两种做法,一是通过
data-target属性关联,还有一种是在 DOM 对象上使用 dataTarget 属性关联。例如一个更换头像功能,点击按钮图像上会出现提示:
HTML代码如下:
<img id="img" src="default.png" alt="默认头像"> <input id="input" type="hidden" data-target="img" required> <button id="button" is="ui-button" type="primary">确认更换</button>
使用了 data-target 将提示定位元素从隐藏的输入框转移为 <img> 图像。此时就可以安心执行
reportValidity()方法了。input.setCustomValidity({ valueMissing: '尚未上传更换的头像' }); button.addEventListener('click', function () { input.reportValidity(); });如果 HTML 设置不方便,也可以使用 DOM 对象进行关联:
<img id="img" src="default.png" alt="默认头像"> <input id="input" type="hidden" required>
input.dataTarget = img; // 其余代码同上...
扩展调用方法
为了更好地适应多种使用场景,本组件还支持下面两种调用方法:
- form.validate() 语法;
- is-validate 自定义属性;
form.validate() 语法
form.validate() 语法仅支持 <form> 元素,使用示意:
<form id="f0">
<input type="search" class="ui-input" required>
<button type="submit" is="ui-button" type="primary">搜索</button>
</form>
f0.validate();
f0.addEventListener('valid', function () {
new Dialog().alert('验证通过');
});
is-validate 自定义属性
给任意的元素设置 is-validate 属性,就会自动进行表单验证初始化的设置。
例如:
<form id="f1" is-validate>
<input type="search" class="ui-input" required>
<button type="submit" is="ui-button" type="primary">搜索</button>
</form>
f1.addEventListener('valid', function () {
new Dialog().alert('同样验证通过');
});
当使用 is-validate属 性语法的时候,支持 connected 和 DOMContentLoaded 生命周期事件。
document.validate对象
document.validate 对象是 Validate 组件的核心,或者说 Validate 组件是基于 document.validate 对象封装而来。
表单验证浏览器内置的三个方法 checkValidity(),reportValidity(),setCustomValidity() 和一个属性 validity 的重置也是基于 document.validate 对象实现。
因此,了解document.validate 对象有助于实现更定制化的需求。
属性
- reg, name
reg的值是内置表单元素类型对应的正则表达式对象合集。默认值是(点击展开):name值是内置表单元素类型对应的中文名称对象合集。默认值是(点击展开):-
我们可以使用这两个属性扩展自定义的验证类型,例如,扩展一个身份证输入框。
document.validate.reg.idcard = '^[1-9]\\d{5}[1-9]\\d{3}((0\\d)|(1[0-2]))(([0|1|2]\\d)|3[0-1])\\d{3}([0-9]|[Xx])$'; document.validate.name.idcard = '身份证';此时,我们就获得了
type="idcard"类型的输入框。点击下面的“验证”按钮,看看提示信息是不是包含了“身份证”关键字:代码如下:
<input id="input" type="idcard" class="ui-input" required> <button id="button" is="ui-button" type="primary">验证</button>
- focusable
- 出错提示出现的时候,输入框的值是否框选。
true表示框选,0和false表示忽略。此属性有成熟的内置逻辑,不建议自己尝试使用。
方法
- selectRange(element, start, end)
- 设置 element 从 start 到 end 位置文字选中。
- getTel(tel)
- 返回准确的连续 11 位号码,过滤短横线或者 +86 这些不需要的字符。
- getType(element)
- 返回元素准确的类型。
- getLength
- 返回元素准确的字符长度。
- getReportText(element)
- 返回元素对应的错误提示信息,如果元素验证通过,没有错误,则返回值是空字符串。
- getTarget(element)
- 返回准确的 ErrorTip 提示的目标元素。
- getMissingState(element)
- 返回元素值是否缺失的验证状态。返回值:
{ // 如果值缺失,下面的false应该是true valueMissing: false } - getMismatchState(element, regex, params)
- 返回元素的值和
type类型对应正则,或者和pattern正则的验证状态。没有错误的返回值是:{ patternMismatch: false, typeMismatch: false } regex和params两个参数表示指定正则表达式,和i,g这类正则参数。本组件并未用到这两个参数。- getRangeState(element)
- 返回元素的值是否超出
max,min以及step属性限定的范围。验证无误的返回值是:{ badInput: false, rangeOverflow: false, rangeUnderflow: false, stepMismatch: false } - getLengthState(element)
- 返回元素的字符数量是否超出
maxlength和minlength的范围限制。验证无误的返回值是:{ tooLong: false, tooShort: false } - getValidity(element)
- 返回元素完整的验证状态结果。就是上面四个方法返回值的合集。另外,会多一个
valid属性,如果为true,表示验证通过,如果为false,则表示验证不通过。规则和规范标准一致。 - isMissing(element)
- 返回布尔值。是否值缺失。
- isMismatch(element)
- 返回布尔值。是否值不匹配。
- isOut(element)
- 返回布尔值。是否值范围超出。
- isOverflow(element)
- 返回布尔值。是否值的字符长度范围超出。
- checkValidity(element)
- 返回布尔值。是否值验证不通过。可以看成是上面四个验证方法的集合。
- setCustomValidity(element, content)
- 设置自定义错误的提示文字。也就是 customError 错误类型的提示内容。其中,
content必需。 - reportValidity(element, content)
- 在准确的位置显示出错提示,并红色高亮元素。其中,
content参数可选。本组件内部并未用到该参数。本方法返回布尔值,表示验证是否成功的结果。 - styleError(element, valid)
- 根据元素的验证状态,显示或关闭出错样式。其中
valid是可选参数,布尔值。如果指定valid的值,则不会再次执行元素的合法性判断,直接使用这个参数值进行样式控制。 - errorTip(element, content)
- 在准确的位置显示出错提示。
原生属性和方法
本组件对原生的表单验证方法进行了重置,以便更好地和 LuLu UI 体系结合。分别如下:
属性
- Element.validity
- 返回当前表单控件元素的验证状态对象。例如:
badInput: false customError: false patternMismatch: false rangeOverflow: false rangeUnderflow: false stepMismatch: false tooLong: false tooShort: false typeMismatch: false valueMissing: false // 验证通过与否 valid: true
- Element.validationMessage
- 返回当前表单控件元素的验证提示信息,如果是空字符串,则表示验证无误
- Element.willValidate
- 当前表单控件元素是否可以参与验证,此属性本组件并未重定义,完全原生,因此,返回值和本组件行为可能会有不一致的地方。例如 hidden 类型的输入框返回值是 false,但是本组件对于hidden类型的输入框也进行了验证。
方法
- Element.checkValidity()
- 返回当前表单控件元素或者表单元素的验证结果。返回值是布尔值。
true表示验证通过。 - Element.reportValidity(content)
- 显示当前表单控件元素的错误提示,如果没有错误,则不出现提示。会使用 LuLu UI 内置的 UI。其中,
content参数可选。 - Element.setCustomValidity(content)
- 设置当前表单控件元素自定义的提示内容。其中,
content参数必需。可以是字符串,表示所有的错误类型都使用这个提示内容;也可以是纯对象,指定具体错误类型的提示内容。例如:input.setCustomValidity({ valueMissing: '主人,请输入内容,喵呜~' });
原生属性和方法虽然使用方便,但是实际开发往往需要即时验证,实时更新输入框的验证状态。此时,还是需要用到 Validate 实例方法。也就是下面要介绍内容。
实例对象属性和方法
实例对象下面两种语法都能获取,如下:
var myValidate = new Validate(eleForm);
或者是:
new Validate(eleForm); var myValidate = eleForm.data.validate;
myValidate暴露了如下属性和方法:
{
// 元素
element: {
form: null
},
// 自定义提示和方法的数据
data: [],
// 是否阻止表单验证,
// 如果为true,表单提交行为不会被阻止
stopValidate: false,
// 一些布尔标志量
params: {
// 是否一次验证多个
multiple: true,
// 是否即时验证
immediate: true,
// 是否提示关键字取自label标签
label: true
},
// 即时验证细节处理
immediate: function () {},
// 返回布尔值,当前表单元素是否验证通过
// 此方法内部有出错提示显示的逻辑处理
checkValidity: function () {},
// 显示出错提示,同Element.reportValidity(content)
reportValidity: function (element, content) {}
}
另外,eleForm 元素上有一个名为 isImmediated 的标志量,表示表单元素是否已经开启了即时校验验证。
更多应用案例
活用label参数优化提示
借助 <label> 元素可以让我们的提示信息更加友好。
例如,点击下面的提交按钮预览效果:
HTML如下:
<form id="commentForm">
<label for="comment"><span>请填写</span>评语:</label>(不超过50字,已输入<label for="comment" class="ui-textarea-count">0</label>字)
<div class="ui-textarea" style="max-width:300px;">
<textarea id="comment" maxlength="50" rows="3" required></textarea>
</div>
<button type="submit" class="ui-button" disabled data-type="primary">提交</button>
</form>
JS就是简单初始化:
// label提示和计数高级用法示意
new Validate('#commentForm', function () {
new Dialog().alert('评论成功', {
type: 'success'
});
});
关于提示优化
在这个例子中,值缺失的提示不是“请填写此字段”,而是“评论不能为空”,显然更友好,那这个友好的提示是从哪里来的呢,JS代码中并没有看到端倪。
实际上是在HTML中设置的,先看关键代码部分:
<label for="comment"><span>请填写</span>评语:</label>
Validate 组件会通过与表单控件元素关联的 <label> 元素寻找提示关键字。关键字提取规则有4个小点:
- 只选取裸露的文字。例如虽然上面
<label>元素完整文本是“请填写此字段”,但是由于“请填写”外面有<span>标签,于是被忽略(请填写外面的 span 是故意添加的); - 会过滤冒号和数字,于是提示关键字不是“评语:”,而是“评语”;
- 多个关联
<label>元素取长度最长的文本作为关键字,因此后面计数的<label>元素会被忽略; - 提示关键字小于2个字符被忽略。
根据近20多个大小项目实践,上面规则没有出现提示异常。
关于计数
输入框有 maxlength 或 minlength 就会触发计数功能。
非表单元素的验证
建议所有的验证和提交走原生的 <form> 元素。
如果遇到特殊场景,无法使用 <form> 元素,不要担心,Validate.js 也是支持的。
参考下面的例子,一个 <input> 框在普通的 <div> 中有个普通的 <button> 标签按钮。
<div id="notForm">
<span class="ui-input ui-search-input">
<input type="search" required>
<span class="ui-icon-search">搜索</span>
</span>
<button is="ui-button" type="primary">搜索</button>
</div>
JS代码如下:
// 非表单元素验证
var notForm = document.querySelector('#notForm');
var notFormValidate = new Validate(notForm, null);
button.addEventListener('click', function () {
if (notFormValidate.checkValidity()) {
new Dialog().alert('无表单验证通过');
}
});
区别在于需要自己绑定点击事件以及回车事件(原生表单内置的,这里回车没支持),然后执行全局验证方法 myValidate.checkValidity(),如果返回是 true,则表示整个表单验证通过。
一开始就开启失焦验证
这种交互和浏览器内置的交互行为是相悖的,我个人是极力反对的,但是架不住一些难搞的产品经理,希望一开始就算用户没提交,也要开始验证。可以执行一次 myValidate.immediate() 方法。
如果产品经理希望一开始就验证,但不希望这么重(上面即时验证开启后,focus 聚焦也会提示),则需要自己添加失焦事件,可以参考下面案例。
// 一开始就失焦验证
var eleBlurForm = document.querySelector('#blurForm');
var blurValidate = new Validate(eleBlurForm, function () {
new Dialog().alert('验证通过');
});
// 添加失焦验证事件处理
eleBlurForm.querySelectorAll('input, select, textarea').forEach(function (eleInput) {
eleInput.addEventListener('blur', function () {
// 需要还没有开启即时验证
if (!eleBlurForm.isImmediated) {
// 提示时候内容不选中
document.validate.focusable = 0;
// 提示
blurValidate.reportValidity(eleInput);
}
});
});
验证成功后走原生提交
这个使用实例对象的 stopValidate属性实现:
new Validate('#submitForm', function (validate) {
validate.stopValidate = true;
this.submit();
});
JS单独触发某个输入框的验证
关键部分源码如下:
验证码:<input id="code" pattern="\d{4}" required>
<button id="button" type="button" class="ui-button" data-type="danger">单独验证验证码</button>
var aloneValidate = new Validate('#validForm');
var eleCode = document.querySelector('#code');
button.addEventListener('click', function () {
aloneValidate.reportValidity(eleCode);
});
除了使用实例对象上的reportValidity()方法,也可以使用控件元素上原生的reportValidity()方法,例如:
button.addEventListener('click', function () {
document.querySelector('#code').reportValidity();
});
使用文字进行错误提示
有时候,设计师设计的出错提示是内嵌在页面中的一段文字,而不是浮层提示,本组件也是可以支持的,但是,虽说支持,其实是有能力实现这样的效果,组件本文并未内置这样的交互支持,因为这样的设计是和 LuLu UI 的理念不相符的。
实现原理:
- 隐藏 ErrorTip 效果;
- 通过输入框的 valid/invalid 事件控制提示文字的显隐;
比方说下面这个例子:
代码示意如下:
/* 隐藏红色出错提示 */
.ui-tips-error {
display: none;
}
<form id="validateForm">
<p>
邮箱:<input type="email" id="email" is="ui-input" name="email" required>
<output id="result" style="color:red;"></output>
</p>
<button type="submit" is="ui-button" type="primary">提交</button>
</form>
new Validate('#validateForm');
// 自定义成功和出错提示处理
email.addEventListener('valid', function () {
result.innerHTML = '';
});
email.addEventListener('invalid', function () {
result.innerHTML = this.validationMessage;
});
本页贡献者:
zhangxinxu