LuLu UI pure版中文文档 »

Fork Me

Validate表单验证

作为插件单独使用

引用下面CSS:

<link rel="stylesheet" href="https://qidian.gtimg.com/lulu/pure/css/common/ui/Tips.css">

JS为:

<script src="https://qidian.gtimg.com/lulu/pure/js/common/ui/Follow.js"></script>
<script src="https://qidian.gtimg.com/lulu/pure/js/common/ui/ErrorTip.js"></script>
<script src="https://qidian.gtimg.com/lulu/pure/js/common/ui/Validate.js"></script>

或者使用合并地址:

<script src="https://qidian.gtimg.com/c/=/lulu/theme/pure/js/common/ui/Follow.js,/lulu/theme/pure/js/common/ui/ErrorTip.js,/lulu/theme/pure/js/common/ui/Validate.js"></script>

基础功能演示

基于HTML5规范的表单验证交互组件。依赖ErrorTip组件。

  • 内置type类型包括:email, tel, url, zipcode, number, date, hour, minute, time, date-range以及month-range。在高版本IE浏览器的向下兼容模式下,会自动将一些type直接text化,这在原生浏览器下是没有问题的。真实用户场景不会存在此问题,如果大家实在拗不过测试同学,可以在type类型后面加个空格,例如type='email '. 这些类型输入框默认内置正则表达式。
  • 自定义正则表达式,使用原生HTML5属性pattern. 例如:pattern="^\d{16}$".
  • 范围超出,如数值输入,range数值范围选择,以及日期和时间的起止限制,使用原生min, max, step属性。
  • 内容长度限制,使用原生maxlength, 以及minlength。本组件会自动选择超出部分的文字。
  • 内置计数和计数验证功能,只要使用UI组件通用的HTML和类名结构(可参考文本框和文本域静态UI组件相关文档)。中英文计数规则使用lang属性控制。没有lang属性,表示中英文1:1; lang="en"表示后面的数值是字符数,1个汉字等于2个英文字符, lang="zh"表示后面的数值是汉字数,2个字母等于1个汉字。
  • 支持指定提示信息和自定义验证方法,具体参见这里

测试兼演示

下面表单采用组件默认验证交互,即点击“提交”按钮后开启即时验证,所有有误内容标红,但是提示tips只会出现在第1个或当前active控件上。

表单验证测试
type='email ', required, 复制:a.b.c@qq.com
type='tel', required, 复制:132-0803-3621
pattern="^\d{16}$", required, 复制:0000111100001111
type='number ', min='5', max='500', required
required
required
您的性别*
type='url'
required
min="2015-07-10", max="2015-12-01"
min="2015-07-10", max="2015-12-01"
minlength="2", maxlength="20", lang="zh"(2个字母1个汉字)
minlength="5", maxlength="200"
 
 
// 表单验证
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>元素中裸露的文字作为提示关键字,会自动过滤冒号,此参数非常实用。
onSuccess Function function () {} 当前表单元素验证通过时候执行的回调。其中上下文this指当前实例对象,支持一个参数,表示当前验证的元素。
onError Function function () {} 当前表单元素验证没有通过的时候执行的回调。其中上下文this指当前实例对象,支持一个参数,表示当前验证的元素。

validate可选参数深入

validate参数实际使用多这样:

new Validate('formId', null, {
    validate: [{
        id: 'inputId',
        report: {
            /* 设置错误类型对应提示 */
        },
        method: function () {
            /* 额外自定义验证方法,返回customError提示文字 */
        }
    }]
});

如果validate参数值的数组项只有一个,可以直接使用该数组项作为参数,示意:

new Validate('formId', null, {
    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 class="ui-button" data-type="primary">点我</button>

点击按钮,会触发验证提示,这里直接使用原生语法示意,如下:

button.addEventListener('click', function () {
    input.reportValidity();
});

如果输入框里面没有值,此时出现的提示文案是内置的“请填写此字段”。

如果我们想要改变出现的提示文案,可以有下面两种方法:

  1. 借助Validate实例方法,我们可以随便找个输入框的祖先元素当作表单元素(实际开发外面需要有<form>元素,这里示意省略了)。
    new Validate(input.parentElement, null, {
        validate: {
            id: 'input',
            report: {
                valueMissing: '主人,这里还没有输入内容,喵~'
            }
        }
    });
    button.addEventListener('click', function () {
        input.reportValidity();
    });

    效果如下:

  2. 使用原生的API语法Element.setCustomValidity(),如下:
    input.setCustomValidity({
        valueMissing: '主人,请输入内容,喵呜~'
    });
    button.addEventListener('click', function () {
        input.reportValidity();
    });

    效果如下:

然而,实际开发,其实使用更多的是借助<label>元素重置宽泛的提示。详见这里

自定义验证方法案例

演示method参数以及隐藏元素的提示功能实现,此参数的基本用法其实在一开始的“测试与演示”案例那里有演示,这里再细化下。

  1. 首先演示下基本功能:

    下面有一个普普通通的输入框和一个普普通通的按钮:

    <input class="ui-input">
    <button class="ui-button" data-type="primary">点我</button>

    此时,我们观察<input>输入框对应的HTML代码,可以看到没有设置required属性,默认状态下,认为是合法的。如果我们希望此时内容为空也提示错误,而HTML又没法设置,怎么办呢?可以使用method参数,JS代码如下:

    new Validate(input.parentElement, null, {
        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();
    });

    不过直接Element.customValidity赋值,如果不是非常特殊场景,建议不要使用,以避免出现意料之外的问题。

  2. 然后演示下隐藏元素的提示,有些表单控件是隐藏在页面中的,例如'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" class="ui-button" data-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;
    // 其余代码同上...

document.validate对象

document.validate对象和Validate组件的核心,或者说Validate组件是基于document.validate对象封装而来。包括对DOM元素重置的

表单验证浏览器内置的3个方法checkValidity()reportValidity()setCustomValidity()和1个属性validity的重置也是基于document.validate对象实现。

因此,了解document.validate对象有助于实现更定制化的需求。

属性

reg, name
reg的值是内置表单元素类型对应的正则表达式对象合集。默认值是(点击展开):
{
    email: '^[a-z0-9._%-]+@([a-z0-9-]+\\.)+[a-z]{2,4}$',
    number: '^\\-?\\d+(\\.\\d+)?$',
    url: "^(http|https)\\:\\/\\/[a-z0-9\\-\\.]+\\.[a-z]{2,3}(:[a-z0-9]*)?\\/?([a-z0-9\\-\\._\\:\\?\\,\\'\\/\\\\\\+&amp;%\\$#\\=~])*$",
    tel: '^1\\d{10}$',
    zipcode: '^\\d{6}$',
    date: '^\\d{4}\\-(0\\d|1[0-2])\\-([0-2]\\d|3[0-1])$',
    time: '^[0-2]\\d\\:[0-5]\\d$',
    hour: '^[0-2]\\d\\:00$',
    minute: '^[0-2]\\d\\:[0-5]\\d$',
    'date-range': '^\\d{4}(\\-\\d{2}){2}\\s至\\s\\d{4}(\\-\\d{2}){2}$',
    'month-range': '^\\d{4}\\-\\d{2}\\s至\\s\\d{4}\\-\\d{2}$'
}
name值是内置表单元素类型对应的中文名称对象合集。默认值是(点击展开):
{
    email: '邮箱',
    tel: '手机号码',
    url: '网址',
    zipcode: '邮编',
    password: '密码',
    number: '数值',
    range: '数值',
    date: '日期',
    year: '年份',
    month: '月份',
    hour: '小时',
    minute: '分钟',
    time: '时间',
    datetime: '日期时间',
    'date-range': '日期范围',
    'month-range': '月份范围'
}

我们可以使用这两个属性扩展自定义的验证类型,例如,扩展一个身份证输入框。

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" class="ui-button" data-type="primary">验证</button>
focusable
出错提示出现的时候,输入框的值是否框选。true表示框选,0false表示忽略。此属性有成熟的内置逻辑,不建议自己尝试使用。

方法

selectRange(element, start, end)
设置element从start到end位置文字选中。
getTel(tel)
返回准确的连续11位号码,过滤短横线或者+86这些不需要的字符。
getType(element)
返回元素准确的类型。
getLength
返回元素准确的字符长度。默认1个字符算1个长度,如果输入框设置lang="zh"属性,则1个汉字算1个长度,2个字母等于1个汉字;如果输入框设置lang="en"属性,则1个英文字符算1个长度,1个汉字等于2个英文字符。

正因为有这样的规则,所以才有此方法。

getReportText(element)
返回元素对应的错误提示信息,如果元素验证通过,没有错误,则返回值是空字符串。
getTarget(element)
返回准确的ErrorTip提示的目标元素。
getMissingState(element)
返回元素值是否缺失的验证状态。返回值:
{
    // 如果值缺失,下面的false应该是true
    valueMissing: false
}
getMismatchState(element, regex, params)
返回元素的值和type类型对应正则,或者和pattern正则的验证状态。没有错误的返回值是:
{
    patternMismatch: false,
    typeMismatch: false
}
regexparams两个参数表示指定正则表达式,和ig这类正则参数。本组件并未用到这两个参数。
getRangeState(element)
返回元素的值是否超出maxmin以及step属性限定的范围。验证无误的返回值是:
{
    badInput: false,
    rangeOverflow: false,
    rangeUnderflow: false,
    stepMismatch: false
}
getLengthState(element)
返回元素的字符数量是否超出maxlengthminlength的范围限制。验证无误的返回值是:
{
    tooLong: false,
    tooShort: false
}
getValidity(element)
返回元素完整的验证状态结果。就是上面4个方法返回值的合集。另外,会多一个valid属性,如果为true,表示验证通过,如果为false,则表示验证不通过。规则和规范标准一致。
isMissing(element)
返回布尔值。是否值缺失。
isMismatch(element)
返回布尔值。是否值不匹配。
isOut(element)
返回布尔值。是否值范围超出。
isOverflow(element)
返回布尔值。是否值的字符长度范围超出。
checkValidity(element)
返回布尔值。是否值验证不通过。可以看成是上面4个验证方法的集合。
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.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
    },
    // 回调
    callback: {
        error: function () {},
        success: function () {}
    };
    // 自定义提示和方法的数据
    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>元素可以让我们的提示信息更加友好。

例如,点击下面的提交按钮预览效果:

<label>提示和计数位置自定义演示
(不超过50字,已输入字)

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个小点:

  1. 只选取裸露的文字。例如虽然上面<label>元素完整文本是“请填写此字段”,但是由于“请填写”外面有<span>标签,于是被忽略(请填写外面的span是故意添加的);
  2. 会过滤冒号和数字,于是提示关键字不是“评语:”,而是“评语”;
  3. 多个关联<label>元素取长度最长的文本作为关键字,因此后面计数的<label>元素会被忽略;
  4. 提示关键字小于2个字符被忽略。

根据近20多个大小项目实践,上面规则没有出现提示异常。

关于计数

Validate组件默认计数交互是配合.ui-input-x > input + .ui-input.ui-textarea-x > input + .ui-textarea的HTML结构实现的,计数内容自动创建,并定位在右下角。

但是,可能有产品不喜欢这样的交互,希望位置在其他地方,本组件也是支持的。两点:

  1. <label>元素的for属性值和输入框元素的id值一致;
  2. 根据输入框类型不同,<label>元素的类名需要包含ui-input-count或者ui-textarea-count

此时就会有自动计数和超过范围时候的颜色高亮提示效果。

如果<label>元素中还有其他内容,则计数字符需要用一个span标签包起来。

<label for="comment" class="ui-textarea-count"><span>0</span>/200</label>

非表单元素的验证

建议所有的验证和提交走原生的<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 class="ui-button" data-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();
});
本页贡献者:

zhangxinxu