使用 HTML 表单验证和约束验证 API
创建 Web 表单一直是一项复杂的任务。虽然表单本身的标记很容易,但检查每个字段是否具有有效且一致的值则更加困难,并且告知用户问题可能会成为一个令人头疼的问题。HTML5 引入了新的表单机制:它为 <input> 元素添加了新的语义类型,并引入了约束验证,以简化客户端检查表单内容的工作。通过设置新属性,无需 JavaScript 即可检查基本的常见约束;可以使用约束验证 API 测试更复杂的约束。
有关这些概念的基本介绍(包括示例),请参阅表单验证教程。
注意:HTML 约束验证不能消除服务器端验证的必要性。尽管预计无效表单请求会少得多,但仍可以通过多种方式发送无效请求
- 通过浏览器的开发人员工具修改 HTML。
- 通过手动创建 HTTP 请求而不使用表单。
- 通过编程方式将内容写入表单(某些约束验证仅针对用户输入运行,而不是在你使用 JavaScript 设置表单字段值时运行)。
因此,你应始终在服务器端验证表单数据,使其与客户端所做的一致。
固有和基本约束
在 HTML 中,基本约束以两种方式声明
语义输入类型
type 属性的固有约束包括
| 输入类型 | 约束描述 | 关联的违规 |
|---|---|---|
<input type="URL"> |
该值必须是 URL 实时标准中定义的绝对 URL。 | TypeMismatch 约束违规 |
<input type="email"> |
该值必须是语法上有效的电子邮件地址,通常格式为 username@hostname.tld,但也可以是本地格式,例如 username@hostname。 |
TypeMismatch 约束违规 |
对于这两种输入类型,如果设置了 multiple 属性,则可以设置多个值,作为逗号分隔列表。如果其中任何一个不满足此处描述的条件,则会触发 类型不匹配 约束违规。
请注意,大多数输入类型没有固有约束,因为有些输入类型被排除在约束验证之外,或者具有将不正确的值转换为正确默认值的净化算法。
与验证相关的属性
除了上面描述的 type 属性外,以下属性用于描述基本约束
| 属性 | 支持该属性的输入类型 | 可能的值 | 约束描述 | 关联的违规 |
|---|---|---|---|---|
pattern
|
text、search、url、tel、email、password |
一个 JavaScript 正则表达式(global、ignoreCase 和 multiline 标志禁用编译) |
该值必须与模式匹配。 |
patternMismatch 约束违规 |
min
|
range、number |
一个有效数字 | 该值必须大于或等于该值。 |
rangeUnderflow 约束违规 |
date、month、week |
一个有效日期 | |||
datetime-local、time |
一个有效日期和时间 | |||
max
|
range、number |
一个有效数字 | 该值必须小于或等于该值 |
rangeOverflow 约束违规 |
date、month、week |
一个有效日期 | |||
datetime-local、time |
一个有效日期和时间 | |||
required
|
text、search、url、tel、email、password、date、datetime-local、month、week、time、number、checkbox、radio、file;也适用于 <select> 和 <textarea> 元素 |
无,因为它是一个布尔属性:它的存在表示真,它的不存在表示假 | 必须有一个值(如果设置)。 |
valueMissing 约束违规 |
step
|
日期 |
一个整数天数 | 除非步长设置为 any 字面量,否则该值必须是 min + 步长的整数倍。 |
stepMismatch 约束违规 |
月份 |
一个整数月数 | |||
周 |
一个整数周数 | |||
datetime-local、time |
一个整数秒数 | |||
range、number |
一个整数 | |||
minlength
|
text、search、url、tel、email、password;也适用于 <textarea> 元素 |
一个整数长度 | 如果非空,则字符数(代码点)不得小于属性值。对于 <textarea>,所有换行符都规范化为单个字符(而不是 CRLF 对)。 |
tooShort 约束违规 |
maxlength
|
text、search、url、tel、email、password;也适用于 <textarea> 元素 |
一个整数长度 | 字符数(代码点)不得超过属性值。 |
tooLong 约束违规 |
约束验证过程
约束验证通过约束验证 API 在单个表单元素上或在表单级别(在 <form> 元素本身上)完成。约束验证以下列方式完成
- 通过调用与表单关联的 DOM 接口(
HTMLInputElement、HTMLSelectElement、HTMLButtonElement、HTMLOutputElement或HTMLTextAreaElement)的checkValidity()或reportValidity()方法,该方法仅评估此元素上的约束,允许脚本获取此信息。checkValidity()方法返回一个布尔值,指示元素的值是否通过其约束。(这通常由用户代理在确定适用哪个 CSS 伪类:valid或:invalid时完成。)相反,reportValidity()方法向用户报告任何约束失败。 - 通过调用
HTMLFormElement接口上的checkValidity()或reportValidity()方法。 - 通过提交表单本身。
调用 checkValidity() 称为静态验证约束,而调用 reportValidity() 或提交表单称为交互式验证约束。
备注
- 如果
<form>元素上设置了novalidate属性,则不会发生约束的交互式验证。 - 调用
HTMLFormElement接口上的submit()方法不会触发约束验证。换句话说,即使表单数据不满足约束,此方法也会将表单数据发送到服务器。请改为调用提交按钮上的click()方法。 minlength和maxlength约束仅在用户提供的输入上进行检查。如果以编程方式设置值,即使显式调用checkValidity()或reportValidity(),也不会检查它们。
使用约束验证 API 的复杂约束
使用 JavaScript 和约束 API,可以实现更复杂的约束,例如,组合多个字段的约束,或涉及复杂计算的约束。
基本思想是在某些表单字段事件(例如 onchange)上触发 JavaScript,以计算约束是否被违反,然后使用 field.setCustomValidity() 方法设置验证结果:空字符串表示约束得到满足,任何其他字符串表示存在错误,并且此字符串是向用户显示错误消息。
组合多个字段的约束:邮政编码验证
邮政编码格式因国家/地区而异。许多国家/地区允许使用可选的国家/地区代码前缀(例如德国的 D-、法国的 F- 和瑞士的 CH-)。一些国家/地区在邮政编码中仅使用固定数量的数字,而其他国家/地区(例如英国)则具有更复杂的格式,允许在某些特定位置使用字母。
注意:这不是一个全面的邮政编码验证库,而是对关键概念的演示。
作为示例,我们将添加一个检查表单约束验证的脚本
<form>
<label for="postal-code">Postal Code: </label>
<input type="text" id="postal-code" />
<label for="country">Country: </label>
<select id="country">
<option value="ch">Switzerland</option>
<option value="fr">France</option>
<option value="de">Germany</option>
<option value="nl">The Netherlands</option>
</select>
<input type="submit" value="Validate" />
</form>
这会显示以下表单
首先,我们编写一个检查约束本身的函数
const countrySelect = document.getElementById("country");
const postalCodeField = document.getElementById("postal-code");
function checkPostalCode() {
// For each country, defines the pattern that the postal code has to follow
const constraints = {
ch: [
"^(CH-)?\\d{4}$",
"Swiss postal codes must have exactly 4 digits: e.g. CH-1950 or 1950",
],
fr: [
"^(F-)?\\d{5}$",
"French postal codes must have exactly 5 digits: e.g. F-75012 or 75012",
],
de: [
"^(D-)?\\d{5}$",
"German postal codes must have exactly 5 digits: e.g. D-12345 or 12345",
],
nl: [
"^(NL-)?\\d{4}\\s*([A-RT-Z][A-Z]|S[BCE-RT-Z])$",
"Dutch postal codes must have exactly 4 digits, followed by 2 letters except SA, SD and SS",
],
};
// Read the country id
const country = countrySelect.value;
// Build the constraint checker
const constraint = new RegExp(constraints[country][0], "");
console.log(constraint);
// Check it!
if (constraint.test(postalCodeField.value)) {
// The postal code follows the constraint, we use the ConstraintAPI to tell it
postalCodeField.setCustomValidity("");
} else {
// The postal code doesn't follow the constraint, we use the ConstraintAPI to
// give a message about the format required for this country
postalCodeField.setCustomValidity(constraints[country][1]);
}
}
然后我们将其链接到 <select> 的 change 事件和 <input> 的 input 事件
countrySelect.addEventListener("change", checkPostalCode);
postalCodeField.addEventListener("input", checkPostalCode);
限制文件上传前的大小
另一个常见的约束是限制要上传的文件的大小。在文件传输到服务器之前在客户端进行检查需要结合约束验证 API,特别是 field.setCustomValidity() 方法,以及另一个 JavaScript API,此处是 File API。
这是 HTML 部分
<label for="fs">Select a file smaller than 75 kB: </label>
<input type="file" id="fs" />
这会显示
JavaScript 读取选定的文件,使用 File.size() 方法获取其大小,将其与(硬编码的)限制进行比较,并调用约束 API 以告知浏览器是否存在违规
const fs = document.getElementById("fs");
function checkFileSize() {
const files = fs.files;
// If there is (at least) one file selected
if (files.length > 0) {
if (files[0].size > 75 * 1000) {
// Check the constraint
fs.setCustomValidity("The selected file must not be larger than 75 kB");
fs.reportValidity();
return;
}
}
// No custom constraint violation
fs.setCustomValidity("");
}
最后,我们将该方法与正确的事件关联起来
fs.addEventListener("change", checkFileSize);
约束验证的视觉样式
除了设置约束外,Web 开发人员还希望控制向用户显示的消息以及它们的样式。
控制元素的样式
元素的样式可以通过 CSS 伪类控制。
:required 和 :optional CSS 伪类
:required 和 :optional 伪类允许编写选择器,匹配具有 required 属性的表单元素,或不具有该属性的表单元素。
:placeholder-shown CSS 伪类
请参阅 :placeholder-shown。
:valid :invalid CSS 伪类
:valid 和 :invalid 伪类用于表示根据输入类型设置,内容分别通过验证和未能通过验证的 <input> 元素。这些类允许用户设置有效或无效表单元素的样式,以便更容易识别格式正确或不正确的元素。
控制约束违规的文本
以下各项有助于控制约束违规的文本
-
以下元素上的
setCustomValidity(message)方法<fieldset>。注意:在大多数浏览器中,对 fieldset 元素设置自定义验证消息不会阻止表单提交。<input><output><select>- 提交按钮(通过类型为
submit的<button>元素或类型为 submit 的input元素创建)。其他类型的按钮不参与约束验证。 <textarea>
-
ValidityState接口描述了上述元素类型的validity属性返回的对象。它表示输入值无效的各种方式。它们共同帮助解释了为什么元素的值无法验证(如果它无效)。