Web 网页的本地客户端验证
众所周知,验证表单是一种痛苦的开发经历。以用户友好、开发人员友好且可访问的方式实现客户端验证是很困难的。在 HTML5 之前,没有办法在本地实现验证;因此,开发人员已经采用了各种基于 JavaScript 的解决方案。
为了帮助减轻开发人员的负担,HTML5 引入了一个称为 约束验证 的概念 - 一种在 Web 表单上实现客户端验证的本机方法。
然而,尽管在所有主要浏览器的最新版本中都可用,但约束验证在很大程度上是一个主题,主要归于演示和演示。我写这篇文章的目的是阐明新的 API,以帮助开发人员为每个人制作更好的 Web 表单。
在本教程中,我将:
- 全面概述什么是约束验证。
- 深入了解规范和浏览器实现的当前限制。
- 现在讨论如何在表单中使用 HTML5 约束验证。
什么是约束验证?
约束验证的核心是在提交表单时浏览器运行的算法,以确定其有效性。为了做出这个决定,该算法利用了新的HTML5属性。 min
, max
, step
, pattern
和 required
以及现有属性 maxlength
和 type
.
例如,采用此形式,其中有一个空的 required
文本输入:
<form>
<input type="text" required value="" />
<input type="submit" value="Submit" />
</form>
如果您尝试按原样提交此表单,支持的 浏览器 将阻止提交并显示以下内容:
- Chrome 21
- 火狐 15
- IE浏览器 10
- Opera 12
- Opera 手机
- 安卓版 Chrome
- 火狐安卓版
根据规范,如何将错误呈现给用户取决于浏览器本身。但是,该规范确实提供了完整的 DOM API、新的 HTML 属性和作者可以用来自定义体验的 CSS 钩子。
DOM API
约束验证 API 将以下属性/方法添加到 DOM 节点。
将验证
这 willValidate
属性指示节点是否为约束验证的候选项。对于 可提交的元素 ,这将设置为 true
除非由于某种原因禁止节点 进行约束验证 ,例如拥有 disabled
属性。
<div></div>
<input type="text" />
<input type="text" disabled />
<script>
document.getElementById('one').willValidate; //undefined
document.getElementById('two').willValidate; //true
document.getElementById('three').willValidate; //false
</script>
有效性
这 validity
DOM 节点的属性返回一个 ValidityState
包含许多与节点中数据有效性相关的布尔属性的对象。
customError
: true
如果每次调用都设置了自定义有效性消息 setCustomValidity()
.
<input />
<input />
<script>
document.getElementById('foo').validity.customError; //false
document.getElementById('bar').setCustomValidity('Invalid');
document.getElementById('bar').validity.customError; //true
</script>
patternMismatch
: true
如果节点的 value
与其不匹配 pattern
属性。
<input pattern="[0-9]{4}" value="1234" />
<input pattern="[0-9]{4}" value="ABCD" />
<script>
document.getElementById('foo').validity.patternMismatch; //false
document.getElementById('bar').validity.patternMismatch; //true
</script>
rangeOverflow
: true
如果节点的 value
大于其 max
属性。
<input type="number" max="2" value="1" />
<input type="number" max="2" value="3" />
<script>
document.getElementById('foo').validity.rangeOverflow; //false
document.getElementById('bar').validity.rangeOverflow; //true
</script>
rangeUnderflow
: true
如果节点的 value
小于其 min
属性。
<input type="number" min="2" value="3" />
<input type="number" min="2" value="1" />
<script>
document.getElementById('foo').validity.rangeUnderflow; //false
document.getElementById('bar').validity.rangeUnderflow; //true
</script>
stepMismatch
: true
如果节点的 value
根据其无效 step
属性。
<input type="number" step="2" value="4" />
<input type="number" step="2" value="3" />
<script>
document.getElementById('foo').validity.stepMismatch; //false
document.getElementById('bar').validity.stepMismatch; //true
</script>
tooLong
: true
如果节点的 value
超过其 maxlength
属性。所有浏览器都通过阻止用户输入超过最大长度的值来防止这种情况实际发生。虽然很少见,但有可能有这个属性 true
在某些浏览器中;我已经写过关于这如何可能的文章 here。
typeMismatch
: true
如果输入节点的 value
根据其无效 type
属性。
<input type="url" value="http://foo.com" />
<input type="url" value="foo" />
<input type="email" value="foo@foo.com" />
<input type="email" value="bar" />
<script>
document.getElementById('foo').validity.typeMismatch; //false
document.getElementById('bar').validity.typeMismatch; //true
document.getElementById('foo2').validity.typeMismatch; //false
document.getElementById('bar2').validity.typeMismatch; //true
</script>
valueMissing
: true
如果节点具有 required
属性,但没有值。
<input type="text" required value="foo" />
<input type="text" required value="" />
<script>
document.getElementById('foo').validity.valueMissing; //false
document.getElementById('bar').validity.valueMissing; //true
</script>
valid
: true
如果上面列出的所有有效性条件都是 false
。
<input type="text" required value="foo" />
<input type="text" required value="" />
<script>
document.getElementById('valid-1').validity.valid; //true
document.getElementById('valid-2').validity.valid; //false
</script>
验证消息
这 validationMessage
DOM 节点的属性包含浏览器在检查节点的有效性并失败时向用户显示的消息。
浏览器为此属性提供默认的本地化消息。如果 DOM 节点不是约束验证的候选项,或者节点包含有效数据 validationMessage
将设置为空字符串。
注意:在撰写本文时,Opera 默认不填写此属性。它将向用户显示正确的错误消息,只是不填写属性。
<input type="text" required />
<script>
document.getElementById('foo').validationMessage;
//Chrome --> 'Please fill out this field.'
//Firefox --> 'Please fill out this field.'
//Safari --> 'value missing'
//IE10 --> 'This is a required field.'
//Opera --> ''
</script>
checkValidity()
checkValidity
表单元素节点上的方法(例如输入、选择、文本区域)返回 true
如果元素包含有效数据。在表单节点上,它返回 true
如果表单的所有子项都包含有效数据。
<form>
<input type="text" required />
</form>
<form>
<input type="text" />
</form>
<script>
document.getElementById('form-1').checkValidity(); //false
document.getElementById('input-1').checkValidity(); //false
document.getElementById('form-2').checkValidity(); //true
document.getElementById('input-2').checkValidity(); //true
</script>
此外,每次检查表单元素的有效性时,都会通过以下方式 checkValidity
并失败,一个 invalid
为该节点触发事件。如果您想在具有 id 的节点上运行某些内容,请使用上面的示例代码 input-1
已检查并包含无效数据,您可以使用以下内容:
document.getElementById('input-1').addEventListener('invalid', function() {
//...
}, false);
没有有效的事件,但是,您可以使用 change
字段有效性更改时通知的事件。
document.getElementById('input-1').addEventListener('change', function(event) {
if (event.target.validity.valid) {
//Field contains valid data.
} else {
//Field contains invalid data.
}
}, false);
setCustomValidity()
这 setCustomValidity
方法更改 validationMessage
属性,并允许您添加自定义验证规则。
因为它正在设置 validationMessage
传入空字符串会将该字段标记为有效,传递任何其他字符串会将该字段标记为无效。不幸的是,没有办法设置 validationMessage
而不会更改字段的有效性。
例如,如果您有两个密码字段要强制相等,则可以使用以下字段:
if (document.getElementById('password1').value != document.getElementById('password2').value) {
document.getElementById('password1').setCustomValidity('Passwords must match.');
} else {
document.getElementById('password1').setCustomValidity('');
}
网页属性
我们已经看到 maxlength
, min
, max
, step
, pattern
和 type
浏览器使用属性来约束数据。对于约束验证,还有两个额外的相关属性 - novalidate
和 formnovalidate
.
无验证
布尔值 novalidate
属性可以应用于表单节点。如果存在此属性,则表示在提交表单时不应验证表单的数据。
<form novalidate>
<input type="text" required />
<input type="submit" value="Submit" />
</form>
因为上面的表格有 novalidate
属性,即使它包含空的必需输入,也会提交。
福尔姆诺验证
布尔值 formnovalidate
属性可以应用于按钮和输入节点,以防止表单验证。例如:
<form>
<input type="text" required />
<input type="submit" value="Validate" />
<input type="submit" value="Do NOT Validate" formnovalidate />
</form>
单击“验证”按钮时,由于输入为空,表单提交将被阻止。但是,当单击“不验证”按钮时,尽管数据无效,表单仍将提交,因为 formnovalidate
属性。
CSS 钩子
编写有效的表单验证不仅仅是错误本身;以可用的方式向用户显示错误同样重要,支持浏览器为您提供了CSS钩子来做到这一点。
:invalid and :valid
在支持浏览器中 :valid
伪类将匹配满足其指定约束的表单元素和 :invalid
伪类将匹配那些不匹配的伪类。
<form>
<input type="text" required />
<input type="text" />
</form>
<script>
document.querySelectorAll('input[type="text"]:invalid'); //Matches input#foo
document.querySelectorAll('input[type="text"]:valid'); //Matches input#bar
</script>
重置默认样式
默认情况下,火狐浏览器会放置一个红色 box-shadow
和 IE10 放置一个红色 outline
上 :invalid
领域。
- 火狐 15
- IE浏览器 10
默认情况下,基于 WebKit 的浏览器和 Opera 不执行任何操作。如果需要一致的起点,可以使用以下命令来禁止显示默认值。
:invalid {
box-shadow: none; /* FF */
outline: 0; /* IE 10 */
}
我有一个待处理 的拉取请求 来讨论这种规范化是否属于规范 化.css 。
内联 Bubbles
较大的显示差异是浏览器在无效字段上显示的内联验证气泡的外观。但是,WebKit 是唯一为您提供自定义气泡的任何方法的呈现引擎。在 WebKit 中,您可以尝试以下 4 个 pseduoclasses 以获得更自定义的外观。
::-webkit-validation-bubble {}
::-webkit-validation-bubble-message {}
::-webkit-validation-bubble-arrow {}
::-webkit-validation-bubble-arrow-clipper {}
删除默认 Bubbles
由于您只能在 WebKit 中自定义气泡的外观,因此如果您希望在所有支持的浏览器中自定义外观,您唯一的选择是抑制默认气泡并实现您自己的气泡。以下内容将禁用页面上所有表单的默认内联验证气泡。
var forms = document.getElementsByTagName('form');
for (var i = 0; i < forms.length; i++) {
forms[i].addEventListener('invalid', function(e) {
e.preventDefault();
//Possibly implement your own here.
}, true);
}
如果您确实禁止显示默认气泡,请确保在表单提交无效后执行某些操作以向用户显示错误。目前,气泡是浏览器指示出现问题的唯一方式。
当前实施问题和限制
虽然这些新的 API 为客户端表单验证带来了很多功能,但您能够执行的操作存在一些限制。
设置自定义有效性
对于简单地设置 validationMessage
字段的 setCustomValidity
有效,但随着形式变得越来越复杂,许多限制 setCustomValidity
方法变得明显。
问题 #1 :在一个字段上处理多个错误
调用 setCustomValidity
在节点上简单地覆盖其 validationMessage
,因此,如果您致电 setCustomValidity
在同一节点上,第二次调用两次将简单地覆盖第一次调用。没有处理错误消息数组的机制,也没有向用户显示多个错误消息的方法。
处理此问题的一种方法是将其他消息附加到节点的 validationMessage
因此。
var foo = document.getElementById('foo');
foo.setCustomValidity(foo.validationMessage + ' An error occurred');
您无法传入 HTML 或格式化字符,因此不幸的是,连接字符串可能会给您留下这样的内容:
问题#2 :知道何时检查字段的有效性
为了说明此问题,请考虑具有两个必须匹配的密码输入字段的表单示例:
<form>
<fieldset>
<legend>Change Your Password</legend>
<ul>
<li>
<label for="password1">Password 1:</label>
<input type="password" required />
</li>
<li>
<label for="password2">Password 2:</label>
<input type="password" required />
</li>
</ul>
<input type="submit" />
</fieldset>
</form>
我之前的建议是使用 change
事件来实现验证,如下所示:
var password1 = document.getElementById('password1');
var password2 = document.getElementById('password2');
var checkPasswordValidity = function() {
if (password1.value != password2.value) {
password1.setCustomValidity('Passwords must match.');
} else {
password1.setCustomValidity('');
}
};
password1.addEventListener('change', checkPasswordValidity, false);
password2.addEventListener('change', checkPasswordValidity, false);
现在,每当用户更改任一密码字段的值时,都将重新评估有效性。但是,请考虑自动填充密码的脚本,甚至是更改约束属性的脚本,例如 pattern
, required
, min
, max
或 step
.这绝对会影响密码字段的有效性,但是,没有事件知道这种情况已经发生。
底线:我们需要一种在字段的有效性可能发生变化时运行代码的方法 might have 。
问题#3 :知道用户何时尝试提交表单
为什么不使用表单的 submit
上述问题的事件?这 submit
在浏览器确定表单包含给定其所有指定约束的有效数据之前,不会触发 EVENT。因此,无法知道用户何时尝试提交表单,并且浏览器阻止了表单。
了解何时发生提交尝试非常有用。您可能希望向用户显示错误消息列表、更改焦点或显示某种帮助文本。不幸的是,您需要一种解决方法才能实现此目的。
实现此目的的一种方法是添加 novalidate
属性并使用其 submit
事件。因为 novalidate
属性 无论数据的有效性如何,都不会阻止表单提交。因此,客户端脚本必须显式检查表单是否包含有效数据 submit
事件并相应地阻止提交。下面是密码匹配示例的扩展,它强制在提交表单之前运行验证逻辑。
<form novalidate>
<fieldset>
<legend>Change Your Password</legend>
<ul>
<li>
<label for="password1">Password 1:</label>
<input type="password" required />
</li>
<li>
<label for="password2">Password 2:</label>
<input type="password" required />
</li>
</ul>
<input type="submit" />
</fieldset>
</form>
<script>
var password1 = document.getElementById('password1');
var password2 = document.getElementById('password2');
var checkPasswordValidity = function() {
if (password1.value != password2.value) {
password1.setCustomValidity('Passwords must match.');
} else {
password1.setCustomValidity('');
}
};
password1.addEventListener('change', checkPasswordValidity, false);
password2.addEventListener('change', checkPasswordValidity, false);
var form = document.getElementById('passwordForm');
form.addEventListener('submit', function() {
checkPasswordValidity();
if (!this.checkValidity()) {
event.preventDefault();
//Implement you own means of displaying error messages to the user here.
password1.focus();
}
}, false);
</script>
这种方法的主要缺点是添加 novalidate
属性可防止浏览器向用户显示内联验证气泡。因此,如果使用此技术,则必须实现自己的向用户显示错误消息的方法。 下面是一个简单的示例 ,显示了实现此目的的一种方法。
底线:我们需要一个 forminvalid
每当表单提交因无效数据而受阻时将触发的事件。
野生动物园
即使 Safari 支持约束验证 API,在撰写本文(版本 6)时,Safari 也不会阻止提交存在约束验证问题的表单。对于用户来说,Safari的行为与根本不支持约束验证的浏览器没有什么不同。
解决此问题的最简单方法是使用与上述解决方法相同的方法,为所有表单提供 novalidate
属性并手动阻止表单提交 preventDefault
.下面的代码将此行为添加到所有窗体。
var forms = document.getElementsByTagName('form');
for (var i = 0; i < forms.length; i++) {
forms[i].noValidate = true;
forms[i].addEventListener('submit', function(event) {
//Prevent submission if checkValidity on the form returns false.
if (!event.target.checkValidity()) {
event.preventDefault();
//Implement you own means of displaying error messages to the user here.
}
}, false);
}
注意:有几个记录在案的错误,其中 checkValidity
方法返回误报(有关 here示例,请参阅此处和 此处 )。使用上述解决方法,误报尤其危险,因为用户将卡在具有有效数据的表单上,因此请谨慎使用。
声明性错误消息
虽然您可以通过设置其 validationMessage
通过 setCustomValidity
,不断设置 JavaScript 样板来实现这一点可能会很麻烦,尤其是在大型表单上。
为了帮助简化此过程,Firefox 引入了一个自定义 x-moz-errormessage
可用于自动设置字段的属性 validationMessage
.
<form>
<input type="text" required x-moz-errormessage="Fill this out." />
<input type="submit" value="Submit" />
</form>
当上述表单在 Firefox 中提交时,用户将看到自定义消息而不是浏览器的默认消息。
此功能曾 向 W3C 提出 ,但被拒绝。因此,目前Firefox是唯一可以声明性指定错误消息的浏览器。
标题属性
虽然它不会改变 validationMessage
,在 patternMismatch
浏览器确实显示 title
属性(如果已提供)。( 注意:Chrome 实际上会显示 title
属性(为任何类型的错误提供),而不仅仅是 patternMismatch
它。 )
例如,如果您尝试提交以下表单:
<form>
<label for="price">Price: $</label>
<input type="text" pattern="[0-9].[0-9][0-9]"
title="Please enter the price in x.xx format (e.g. 3.99)"
value="3" />
<input type="submit" value="Submit" />
</form>
以下是浏览器将显示的内容的示例:
- Chrome 21
- 火狐 15
- Opera 12
- IE 10
:无效和 :有效
如前所述, :valid
伪类将匹配满足所有指定约束的表单元素和 :invalid
伪类将匹配那些不匹配的伪类。 遗憾的是,这些伪类将在提交表单之前和与表单交互之前立即匹配。请考虑以下示例:
<style>
:invalid {
border: 1px solid red;
}
:valid {
border: 1px solid green;
}
</style>
<form>
<input type="text" required />
<input type="text" />
</form>
这里的目标是简单地在无效字段周围放置一个红色边框,在有效字段周围放置一个绿色边框,它会在呈现表单时立即这样做。但是 ,内联表单验证的可用性测试 表明,向用户提供反馈的最佳时间是在 after 他们与字段交互之后立即,而不是之前。
使用上面的示例实现此目的的一种方法是在与输入交互后向输入添加一个类,并且仅在类存在时才应用边框。
<style>
.interacted:invalid {
border: 1px solid red;
}
.interacted:valid {
border: 1px solid green;
}
</style>
<form>
<input type="text" required />
<input type="text" />
<input type="submit" />
</form>
<script>
var inputs = document.querySelectorAll('input[type=text]');
for (var i = 0; i < inputs.length; i++) {
inputs[i].addEventListener('blur', function(event) {
event.target.classList.add('interacted');
}, false);
}
</script>
Firefox 已经看到了这些问题,并 实现了另外两个伪类 - :-moz-ui-invalid
和 :-moz-ui-valid
.与 :invalid
和 :valid
, :-moz-ui-invalid
和 :-moz-ui-valid
在修改字段或用户尝试提交输入无效的表单之前,不会匹配字段。
基于这项工作, CSS 选择器级别 4 规范 包含一个 :user-error
功能很像的伪类 :-moz-ui-invalid
.它尚未被任何浏览器实现。
最后一点。请注意 :valid
和 :invalid
应该匹配 <form>
节点,以及表单元素。目前,这仅在 Firefox 中实现,但在将全局规则应用于 :valid
和 :invalid
.
不支持的浏览器
虽然浏览器对约束验证的支持非常好,但仍有一些主要参与者不存在,最值得注意的是IE <= 9,iOS Safari和默认的Android浏览器。
处理不受支持的浏览器
如果您打算在生产 Web 表单中使用新的约束验证 API,则必须 something 对不受支持的浏览器执行一些操作。根据我的经验,有两个主要选择:
选项 1 - 仅依赖服务器端验证
请务必记住,即使使用这些新的 API,客户端验证也不会消除对服务器端验证的需求。恶意用户可以轻松解决任何客户端约束,并且 HTTP 请求不必来自浏览器。
因此,客户端验证应始终被视为对用户体验的渐进增强;即使客户端验证不存在,所有窗体也应可用。
由于无论如何都必须进行服务器端验证,因此如果您只是让服务器端代码返回合理的错误消息并将其显示给最终用户,那么您就为不支持任何形式的客户端验证的浏览器提供了内置的回退。
选项 2 - 填充材料
虽然依赖服务器端验证对于某些应用程序来说是合理的,但对于许多人来说,在不支持的浏览器中为用户放弃客户端验证的可用性优势根本不是一种选择。
如果要使用新的 API 编写代码并希望它在任何地方工作,实现此目的的最佳方法是使用 polyfill 。除了使 API 在不支持的浏览器中工作之外,许多 polyfill 还采取了额外的步骤来解决本机实现中的一些问题。
填充物
有许多 polyfill 允许在任何浏览器中使用约束验证 API。我将讨论两个更受欢迎的选项。
网垫片
Webshims 是polyfill的集合,包括HTML5表单和约束验证API的填充。
为了展示如何使用 Webshims,让我们回到带有单个表单的原始示例 required
文本输入。
<form>
<input type="text" required value="" />
<input type="submit" value="Submit" />
</form>
要使这在所有浏览器中一致地工作,您需要包含 Webshims 及其依赖项,然后调用 $.webshims.polyfill('forms')
.
<!-- Webshims' dependencies -->
<script src="js/jquery-1.8.2.js"></script>
<script src="js/modernizr-yepnope-custom.js"></script>
<!-- Webshims base -->
<script src="js-webshim/minified/polyfiller.js"></script>
<script>jQuery.webshims.polyfill('forms');</script>
<form>
<input type="text" required value="" />
<input type="submit" value="Submit" />
</form>
在支持浏览器中,结果不会有区别。但是,本机不支持约束验证的浏览器现在可以阻止无效的表单提交,并在自定义气泡中显示错误消息。例如,以下是用户将在Safari和IE8中看到的内容。
- 野生动物园 6
- IE浏览器 8
除了填充码之外,Webshims 还为前面讨论的约束验证的许多限制提供了解决方案。
- 通过
data-errormessage
属性。 - 提供类
form-ui-valid
和form-ui-invalid
其工作原理类似于:-moz-ui-valid
和:-moz-ui-invalid
. - 提供自定义事件
firstinvalid
,lastinvalid
,changedvalid
和changedinvalid
. - 包括表单提交时 WebKit 误报的解决方法。
有关Webshims提供的内容的更多信息,请参阅其 HTML5表单文档 。
H5F
H5F 是一个轻量级、无依赖关系的填充物,它实现了完整的约束验证 API 以及许多新属性。
要了解如何使用 H5F,让我们将其添加到具有单个表单的基本示例中 required
文本输入。
<script src="H5F.js"></script>
<form>
<input type="text" required value="" />
<input type="submit" value="Submit" />
</form>
<script>
H5F.setup(document.getElementsByTagName('form'));
</script>
H5F 将阻止在所有浏览器中提交表单,但用户只会在本机支持表单的浏览器中看到内联验证气泡。由于 H5F 填充了完整的约束验证 API,因此您可以使用它来实现自己的 UI 来执行所需的任何操作。
如果您正在寻找一些如何利用 API 的示例 ,H5F 的演示页面 有一个实现,可以在输入右侧的气泡中显示消息。我还有一个示例,展示了如何在 列表中显示所有错误消息 表单顶部的
除了填充约束验证 API 之外,H5F 还提供了要模拟的类 :-moz-ui-valid
和 :-moz-ui-invalid
.默认情况下,这些类是 valid
和 error
尽管可以通过将第二个参数传递给 H5F.setup
.
H5F.setup(document.getElementById('foo'), {
validClass: 'valid',
invalidClass: 'invalid'
});
有关H5F的更多信息,请查看 Github。
结论
HTML5的约束验证API可以快速向表单添加客户端验证,同时提供JavaScript API和CSS钩子进行自定义。
虽然实现和旧浏览器仍然存在一些问题需要处理,但通过良好的 polyfill 或服务器端回退,您现在可以在表单中使用这些 API。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论