LuLu UI Edge版中文文档 » Validate表单验证

文档 Github➹

Validate表单验证

安装与调用

引用下面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。内容长度超出提示的时候会自动选择超出部分的文字。
  • 支持指定提示信息和自定义验证方法,具体参见这里

测试兼演示

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

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="2023-07-10", max="2023-12-01"
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> 元素中裸露的文字作为提示关键字,会自动过滤冒号,此参数非常实用。

事件

参与表单验证的所有控件元素均支持原生的 validinvalid 事件,分别在验证通过和验证不通过的时候触发(本文档页面对底部案例有使用示意)。

另外,当所有表单控件元素都验证通过的时候,会触发表单元素自定义的 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();
});

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

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

  1. 借助Validate实例方法,我们可以随便找个输入框的祖先元素当作表单元素(实际开发外面需要有<form>元素,这里示意省略了)。
    new Validate(input.parentElement, {
        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 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();
    });
  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" 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;
    // 其余代码同上...

扩展调用方法

为了更好地适应多种使用场景,本组件还支持下面两种调用方法:

  1. form.validate() 语法;
  2. 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属 性语法的时候,支持 connectedDOMContentLoaded 生命周期事件。

document.validate对象

document.validate 对象是 Validate 组件的核心,或者说 Validate 组件是基于 document.validate 对象封装而来。

表单验证浏览器内置的三个方法 checkValidity()reportValidity()setCustomValidity() 和一个属性 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$'
}
name值是内置表单元素类型对应的中文名称对象合集。默认值是(点击展开):
{
    email: '邮箱',
    tel: '手机号码',
    url: '网址',
    zipcode: '邮编',
    password: '密码',
    number: '数值',
    range: '数值',
    date: '日期',
    year: '年份',
    month: '月份',
    hour: '小时',
    minute: '分钟',
    time: '时间'
}

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

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 表示框选,0false 表示忽略。此属性有成熟的内置逻辑,不建议自己尝试使用。

方法

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
}
regexparams 两个参数表示指定正则表达式,和ig这类正则参数。本组件并未用到这两个参数。
getRangeState(element)
返回元素的值是否超出 maxmin 以及 step 属性限定的范围。验证无误的返回值是:
{
    badInput: false,
    rangeOverflow: false,
    rangeUnderflow: false,
    stepMismatch: false
}
getLengthState(element)
返回元素的字符数量是否超出 maxlengthminlength 的范围限制。验证无误的返回值是:
{
    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> 元素可以让我们的提示信息更加友好。

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

<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多个大小项目实践,上面规则没有出现提示异常。

关于计数

输入框有 maxlengthminlength 就会触发计数功能。

非表单元素的验证

建议所有的验证和提交走原生的 <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 的理念不相符的。

实现原理:

  1. 隐藏 ErrorTip 效果;
  2. 通过输入框的 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