表单验证第一部分:使用 HTML 和 CSS技巧对表单进行约束验证

发布于 大漠

本文转载自:众成翻译 译者:cherryjin 链接:http://www.zcfy.cc/article/3523 原文:https://css-tricks.com/form-validation-part-1-constraint-validation-html/

大多数的JavaScript表单验证类库体积都非常庞大,而且往往需要引入其他的库,像jQuery。 例如,MailChimp的嵌入式表单,包含了140kb的 验证文件(压缩后)。它引入了整个jQuery库,以及一个第三方的表单验证插件,还有一些原生的MaiChimp代码。实际上,正是MailChimp的嵌入式表单引发了我对于现代表单验证的一系列思考。我们现在有哪些新工具可以进行表单验证?哪些是可行的?哪些又是仍然需要的?

在这个系列,我将会向你展示两种轻量级方法来验证前端的表单。这两个方法都集合了较新的web API。随后,我也会向你介绍如何使IE9以下版本的浏览器也支持这些API。(这会为你提供全球网络流量99.6%的覆盖率).

最后,我们来看一下MailChimp的注册表单,并用少于28行的代码来实现它。

注意,前端进行的表单验证是可以被绕过的,因此你需要在服务器端再次进行验证。

好了,让我们开始吧。

文章系列:

最简单的验证方法: 约束验证

通过输入类型(例如,<input type='email'>)以及验证属性(例如requiredpatten), 浏览器可以自行对用户输入的内容进行验证,以及在输入内容不符合验证规则时发出警告。

想要查看浏览器支持的输入类型以及属性可以查看这篇文章 varies wildly from browser to browser, 不过, 我也会提供一些技巧和方法来最大化的保证浏览器的兼容性。

基础的文本验证

假设你有一个文本字段,用户在提交表单之前必须要填充这个字段. 这时可以给输入框添加 required 属性,当用户没有填充此字段时,支持该属性的浏览器就会发出警告,并阻止表单的提交。

<input type="text" required>

拥有required属性的文本框在Chrome浏览器下的显示。

你需要设置输入内容的字数的最大或最小限制吗? 使用 minlengthmaxlength 属性可以做到这一点。 下面这个例子就展示了限制输入内容的长度在3-12个字符内

<input type="text" minlength="3" maxlength="12">

当输入的字符长度不在所规定的范围内,在Firefox浏览器下就会显示此错误信息。

pattern 属性允许你定义一个正则表达式对所输入的内容进行验证。例如,假设你规定输入的密码必须包含一个大写字符,一个小写字符以及一个数字。浏览器会根据正则表达式对所输入的内容进行验证

<input type="password" pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$" required>

当输入的密码不符合规定时在Safari浏览器下会显示此错误信息。

如果你在定义了pattern的同时也定义了title属性,那么当正则表达式匹配不成功时title属性的值会包含在错误信息提示框内。

<input type="password" pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$" title="Please include at least 1 uppercase character, 1 lowercase character, and 1 number." required>

在 Opera浏览器下显示的错误信息提示框, 其中包含title属性的值,可以以此来作为正则表达式的补充说明。

你也可以结合 minlength和 (银行似乎就是这样, maxlength) 给输入的内容设定一个最小或最大的长度值。

<input type="password" minlength="8" pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$" title="Please include at least 1 uppercase character, 1 lowercase character, and 1 number." required>

查看 Chris Ferdinandi的演示 Form Validation:Basic Text (@cferdinandi)在 CodePen上。

验证数字

number输入类型只允许用户输入数字。 浏览器会拒绝接收字母或者其他的字符,以及在用户使用它们的时候发出警告。浏览器支持 input[type="number"], 但是你也可以提供一个 pattern 作为备用。

<input type="number" pattern="[-+]?[0-9]">

默认情况下, number 类型的输入框只允许输入整数。

你也可以通过设置step 属性来允许接收浮点数 (数字与小数) 。 告诉浏览器你想接收的数字间隔。 这个间隔可以是任何数值 (例如, 0.1 ), 或者设置为 any 如果你想接收任何数字的话。

你可以通过 pattern来设置允许接收的位数。

<input type="number" step="any" pattern="[-+]?[0-9]*[.,]?[0-9]+">

如果想要给输入的数字设定范围, 可以通过设置 minmax 属性让浏览器来验证. 你可以修改你的 pattern去进行匹配。例如,假设输入的数字在342之间, 你可以这样做:

<input type="number" min="3" max="42" pattern="[3-9]|[1-3][0-9]|4[0-2]">

查看Chris Ferdinandi的演示 Form Validation: Numbers(@cferdinandi) 在 CodePen上。

验证Email地址和URL

email输入类型会在用户输入不合法的Email地址时给出警告。像 number 输入类型一样, 你需要给浏览器提供一个pattern来告诉浏览器不支持哪些输入类型。

Email 验证的正则表达式一直以来都是个热议话题。为了寻找符合RFC822规范的,我测试了许多正则表达式。下面使用的这个, 由 Richard Willis所写, 是我发现的最好的一个。

<input type="email" pattern="^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$">

email 输入类型允许email地址没有TLD ( "email@example.com"中的 "example.com"). 这是由于email地址规范 RFC822, 允许本地电子邮件不需要顶级域名。

如果你想要接收TLD (你可能会这么做),你可以修改 pattern强制规定输入的email地址需要 域名扩展,就像下面这样:

<input type="email" title="The domain portion of the email address is invalid (the portion after the @)." pattern="^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*(\.\w{2,})+$">

同样的, url输入类型会在用户输入不合法的URL地址时给出警告。不过, 你应该为那些不支持url类型输入框的浏览器提供一个pattern。下面使用的这个pattern改编自 Diego Perini的一个项目, 是我迄今为止遇到的最强大的一个。

<input type="url" pattern="^(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*)(?::\d{2,})?(?:[\/?#]\S*)?$">

email 输入类型一样, url也允许不输入TLD.如果你不想接收本地URL, 你可以更新pattern来检查TLD , 就像这样。

<input type="url" title="The URL is a missing a TLD (for example, .com)." pattern="^(?:(?:https?|HTTPS?|ftp|FTP):\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)(?:\.(?:[a-zA-Z\u00a1-\uffff0-9]-*)*[a-zA-Z\u00a1-\uffff0-9]+)*(?:\.(?:[a-zA-Z\u00a1-\uffff]{2,}))\.?)(?::\d{2,})?(?:[/?#]\S*)?$">

查看 Chris Ferdinandi的演示Form Validation: Email & URLs(@cferdinandi) 在 CodePen上。

验证日期

这有一些非常酷的输入类型,不但可以验证日期而且可以提供原生的日期选择器. 不幸的是, 只有Chrome, Edge, 和 Mobile Safari 支持这个属性. (我已经期待了Firefox支持这个功能好多年了 更新: 这个功能有希望在不久的将来被 Firefox 支持) 其他的浏览器只会将它视为一个文本域进行显示.

一如既往, 我们可以为不支持该类型的浏览器提供一个pattern .

date 输入类型的格式是标准的日/月/年。

<input type="date" pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))">

在支持该类型的浏览器中, 选定的日期会显示成这样: MM/DD/YYYY (注意: 这是处于美国所显示的格式.其他国家/地区的用户以及修改日期设置的用户的显示可能会有所不同)。但是 value 实际上是这种格式: YYYY-MM-DD

你应该为使用不支持该类型输入框的浏览器用户提供一个指导,像这样, "请使用 YYYY-MM-DD 格式." 然而, 你并不想要那些使用Chromn或 Mobile Safari的用户看到这个提示 , 因为这并不是他们会看到的格式, 这样会造成困扰。

查看Chris Ferdinandi关于 [Form Validation: Dates]的演示(@cferdinandi) 在 CodePen上。

一个简单的功能测试

我们可以写一个功能测试来检测该输入类型的支持情况. 我们可以创建一个input[type="date"] 元素, 并为其添加一个不合法的日期值, 观察浏览器是否会对此采取措施 。然后,你可以隐藏支持date 输入类型的浏览器所显示的描述性文字 。

<label for="date">Date <span class="description-date">YYYY-MM-DDD</span></label>
<input type="date" id="date" pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-9])|(?:(?!02)(?:0[1-9]|1[0-2])-(?:30))|(?:(?:0[13578]|1[02])-31))">

<script>
    var isDateSupported = function () {
        var input = document.createElement('input');
        var value = 'a';
        input.setAttribute('type', 'date');
        input.setAttribute('value', value);
        return (input.value !== value);
    };

    if (isDateSupported()) {
        document.documentElement.className += ' supports-date';
    }
</scipt>

<style>
    .supports-date .description-date {
        display: none;
    }
</style>

查看 Chris Ferdinandi关于Form Validation:Dates with a Feature Test]的演示(@cferdinandi) 在 CodePen上。

其他的日期类型

time 输入类型可以让用户选择一个时间,而 month输入类型可以让他们从年月选择器中选择月份 .再次强调, 我们需要为那些不支持这些输入类型的浏览器提供一个pattern

<input type="time" pattern="(0[0-9]|1[0-9]|2[0-3])(:[0-5][0-9])">
<input type="month" pattern="(?:19|20)[0-9]{2}-(?:(?:0[1-9]|1[0-2]))">

time 输入以12小时 am/pm 的格式进行显示, 但是 value24小时制时间格式。month 输入在支持它的浏览器上以 May 2017 的格式进行显示,但是值是YYYY-MM 格式。

就像input[type="date"], 支持这些类型的浏览器会隐藏格式,因此你应该提供一个格式描述

查看Chris Ferdinandi的演示 Form Validation: Add novalidate programatically(@cferdinandi) 在 CodePen上。

这看起来非常容易.卖什么关子啊?

尽管这些约束验证的API非常简单并且轻量级, 但它仍然具有一些缺点

你可以通过:invalid伪选择器对出现错误的域设置样式, 但是,你不能对错误信息设置样式.

各浏览器间的行为也不一致. Chrome浏览器在你提交表单之前不会显示任何错误信息。Firefox 浏览器会在该域失去焦点时显示一条红线,但是只有在用户鼠标悬停在此域上时才会显示错误信息 (基于 WebKit的浏览器会一直保持错误)。

Christian HolstLuke Wroblewski (这是分开的) 的用户发现,当离开一个字段时会显示错误信息,并且浏览器保持该错误,直到这个字段的问题被修复, 这提供了最好和最快的用户体验. 额外的 CSS 技巧: 只有在这些字段当前不处于编辑模式时利用invalid选择器来为它们设定样式 :not(:focus):invalid { }

不幸的是,默认情况下,所有的浏览器都不会正常的运行。

在这个系列的下一篇文章中, 我会向你展示如何利用原生的约束验证API以及一点JavaScript来实现我们想要的用户体验。 没有第三方库的引入!

文章系列:

  • 在HTML中的约束验证
  • 用JavaScript编写约束验证API
  • 一个Validity State API Polyfill
  • 验证MailChimp订阅表单Air Jordan XXXII 32 Shoes