表单验证第一部分:使用 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'>
)以及验证属性(例如required
和patten
), 浏览器可以自行对用户输入的内容进行验证,以及在输入内容不符合验证规则时发出警告。
想要查看浏览器支持的输入类型以及属性可以查看这篇文章 varies wildly from browser to browser, 不过, 我也会提供一些技巧和方法来最大化的保证浏览器的兼容性。
基础的文本验证
假设你有一个文本字段,用户在提交表单之前必须要填充这个字段. 这时可以给输入框添加 required
属性,当用户没有填充此字段时,支持该属性的浏览器就会发出警告,并阻止表单的提交。
<input type="text" required>
拥有required
属性的文本框在Chrome浏览器下的显示。
你需要设置输入内容的字数的最大或最小限制吗? 使用 minlength
和 maxlength
属性可以做到这一点。 下面这个例子就展示了限制输入内容的长度在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]+">
如果想要给输入的数字设定范围, 可以通过设置 min
和max
属性让浏览器来验证. 你可以修改你的 pattern
去进行匹配。例如,假设输入的数字在3
和42
之间, 你可以这样做:
<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
的格式进行显示, 但是 value
是24
小时制时间格式。month
输入在支持它的浏览器上以 May 2017
的格式进行显示,但是值是YYYY-MM
格式。
就像input[type="date"]
, 支持这些类型的浏览器会隐藏格式,因此你应该提供一个格式描述
查看Chris Ferdinandi的演示 Form Validation: Add novalidate
programatically(@cferdinandi) 在 CodePen上。
这看起来非常容易.卖什么关子啊?
尽管这些约束验证的API非常简单并且轻量级, 但它仍然具有一些缺点
你可以通过:invalid
伪选择器对出现错误的域设置样式, 但是,你不能对错误信息设置样式.
各浏览器间的行为也不一致. Chrome浏览器在你提交表单之前不会显示任何错误信息。Firefox 浏览器会在该域失去焦点时显示一条红线,但是只有在用户鼠标悬停在此域上时才会显示错误信息 (基于 WebKit的浏览器会一直保持错误)。
Christian Holst 和 Luke Wroblewski (这是分开的) 的用户发现,当离开一个字段时会显示错误信息,并且浏览器保持该错误,直到这个字段的问题被修复, 这提供了最好和最快的用户体验. 额外的 CSS 技巧: 只有在这些字段当前不处于编辑模式时利用invalid选择器来为它们设定样式 :not(:focus):invalid { }
。
不幸的是,默认情况下,所有的浏览器都不会正常的运行。
在这个系列的下一篇文章中, 我会向你展示如何利用原生的约束验证API以及一点JavaScript来实现我们想要的用户体验。 没有第三方库的引入!