HTML表单与约束验证完全指南

本文探讨了HTML表单字段及其在HTML5中所提供的验证选项,同时还将介绍如何通过CSS和JavaScript来增强这些功能。

什么是约束验证?

每个表单字段都有其特定目的,这一目的通常受限于约束——即规定每个表单字段应输入什么及不应输入什么的规则。例如,email字段要求输入有效的电子邮件地址;password字段可能要求特定类型的字符并设定最小字符数;而文本字段则可能限制可输入的字符数量。

现代浏览器具备检查用户是否遵守这些约束的能力,并在规则被违反时发出警告。这一过程被称为约束验证

客户端与服务器端验证

JavaScript语言早期,大多数编写的代码用于处理客户端表单验证。即使现在,开发者仍需花费大量时间编写检查字段值的函数。在现代浏览器中,这还是必需的吗?可能并非如此。多数情况下,这取决于你的具体需求。

但首先,请注意一个重要警告:

客户端验证是一种便利功能,它能防止应用在浪费时间和带宽向服务器发送数据之前出现常见的数据输入错误。但这绝不能替代服务器端验证!

始终在服务器端对数据进行消毒处理。并非所有请求都来自浏览器。即便来自浏览器,也无法保证浏览器已对数据进行了验证。任何懂得如何打开浏览器开发者工具的人,也能轻易绕过你精心设计的HTML和JavaScript。

HTML5输入字段

HTML提供了:

但你最常使用的是<input>

<input type="text" name="username" />

通过type属性设置控件类型,选项众多:

type description
button a button with no default behavior
checkbox a check/tick box
color a color picker
date a date picker for the year, month, and day
datetime-local a date and time picker
email an email entry field
file a file picker
hidden a hidden field
image a button which displays the image defined by the src attribute
month a month and year picker
number a number entry field
password a password entry field with obscured text
radio a radio button
range a slider control
reset a button that resets all form inputs to their default values (but avoid using this, as it’s rarely useful)
search a search entry field
submit a form submission button
tel a telephone number entry field
text a text entry field
time a time picker with no time zone
url a URL entry field
week a week number and year picker

若省略type属性或浏览器不支持某选项,浏览器会默认回退至text。现代浏览器对所有类型支持良好,但旧版浏览器仍会显示文本输入框。

其他有用的<input>属性包括:

attribute description
accept file upload type
alt alternative text for the image types
autocomplete hint for field auto-completion
autofocus focus field on page load
capture media capture input method
checked checkbox/radio is checked
disabled disable the control (it won’t be validated or have its value submitted)
form associate with a form using this ID
formaction URL for submission on submit and image buttons
inputmode data type hint
list ID of <datalist> autocomplete options
max maximum value
maxlength maximum string length
min minimum value
minlength minimum string length
name name of control, as submitted to server
pattern a regular expression pattern, such as [A-Z]+ for one or more uppercase characters
placeholder placeholder text when the field value is empty
readonly the field is not editable, but it will still be validated and submitted
required the field is required
size the size of the control (often overridden in CSS)
spellcheck set true or false spell-checking
src image URL
step incremental values in numbers and ranges
type field type (see above)
value the initial value

HTML输出字段

HTML5不仅提供输入类型,还提供只读输出:

  • output:计算结果或用户操作的文本反馈
  • progress:具有valuemax属性的进度条
  • meter:一种刻度,可根据valueminmaxlowhighoptimum属性的设定值在绿色、琥珀色和红色之间变化。

输入标签

字段应有关联的<label>,可通过将其包裹在元素周围实现:

<label>your name <input type="text" name="name" /><label>

或者通过for属性将字段的id链接到标签:

<label for="nameid">your name</label>
<input type="text" id="nameid" name="name" />

标签对可访问性至关重要。您可能遇到过使用placeholder节省屏幕空间的表单:

<input type="text" name="name" value="" placeholder="your name" />

占位符文本一旦用户输入内容——哪怕只是一个空格——就会消失。显示标签比强迫用户记住字段要求更好!

输入行为

字段类型和约束属性会改变浏览器的输入行为。例如,number输入在移动设备上显示数字键盘。该字段可能显示一个微调控件,键盘上的上下箭头按键将增减数值。

大多数字段类型显而易见,但也有例外。例如,信用卡号虽是数字,但使用增减微调控件并无意义,且在输入16位数字时容易误触上下键。更佳做法是采用标准text类型,同时设置inputmode属性numeric,以显示合适的键盘。设置autocomplete="cc-number"还能提示预设或曾输入过的信用卡号。

正确使用字段typeautocorrect能带来难以通过JavaScript实现的好处。例如,某些移动浏览器支持:

自动验证

浏览器确保输入值符合由typeminmaxstepminlengthmaxlengthpatternrequired等属性定义的约束。例如:

<input type="number" min="1" max="100" required />

尝试提交空值会阻止表单提交,并在Chrome中显示以下消息:

微调器不允许超出1到100范围的数值。如果你输入的不是数字的字符串,也会显示类似的验证提示。这一切都不需要任何一行JavaScript代码。

你可以通过以下方式禁用浏览器验证:

  • <form>元素中添加novalidate属性
  • 在提交按钮或图片上添加formnovalidate属性

自定义JavaScript输入

如果你正在编写一个新的基于JavaScript的日期输入组件,请停下来,离开键盘!

编写自定义输入控件是困难的。你必须考虑鼠标、键盘、触摸、语音、可访问性、屏幕尺寸,以及当JavaScript失败时会发生什么。你也在创造一个不同的用户体验。也许你的控件在桌面、iOS和Android上比标准的日期选择器更优秀,但陌生的用户界面会让一些用户感到困惑。

开发者选择创建基于JavaScript的输入有三个主要原因。

1. 标准控件难以样式化

CSS样式化受限,经常需要使用如覆盖输入与其标签的::before::after伪元素这样的技巧。情况正在改善,但对于那些优先考虑形式而非功能的设计,应持怀疑态度。

2. 旧浏览器不支持现代的<input>类型

本质上,你是在为Internet Explorer编写代码。IE用户不会获得日期选择器,但仍然可以按照YYYY-MM-DD格式输入日期。如果客户坚持要求,那么仅在IE中加载一个polyfill即可。无需让现代浏览器承担这个负担。

3. 你需要一个从未被实现过的新输入类型

这种情况虽不常见,但始终应从适当的HTML5字段开始。它们快速且在脚本加载之前就能工作。你可以根据需要逐步增强这些字段。例如,一点JavaScript代码就能确保日历事件的结束日期晚于开始日期。

总结来说:避免重新发明HTML控件!

CSS验证样式

你可以将以下伪类应用于输入字段,根据当前状态对其进行样式设置:

selector description
:focus the field with focus
:focus-within an element contains a field with focus (yes, it’s a parent selector!)
:focus-visible an element has focus owing to keyboard navigation, so a focus ring or more evident styling is necessary
:required a field with a required attribute
:optional a field without a required attribute
:valid a field that has passed validation
:invalid a field that has not passed validation
:user-valid a field that has passed validation after the user has interacted with it (Firefox only)
:user-invalid a field that hasn’t passed validation after the user has interacted with it (Firefox only)
:in-range the value is within range on a number or range input
:out-of-range the value is out of range on a number or range input
:disabled a field with a disabled attribute
:enabled a field without a disabled attribute
:read-only a field with a read-only attribute
:read-write: a field without a read-only attribute
:checked a checked checkbox or radio button
:indeterminate an indeterminate checkbox or radio state, such as when all radio buttons are unchecked
:default the default submit button or image

你可以使用::placeholder伪元素来设置输入框的placeholder文本样式:

/* 电子邮件字段上的蓝色占位符 */
input[type="email"]::placeholder {
  color: blue;
}

上述选择器具有相同的特异性,因此顺序可能很重要。考虑这个例子:

input:invalid { color: red; }
input:enabled { color: black; }

无效输入的文本为红色,但它仅应用于具有disabled属性的输入——所以所有启用的输入文本都是黑色。

浏览器在页面加载时应用验证样式。例如,在下面的代码中,每个无效字段都会被赋予红色边框:

:invalid {
  border-color: #900;
}

用户在未与表单互动前,面对的是一排醒目的红色警告框。若能在首次提交或值变更时即时显示验证错误,将大大提升用户体验。这时,JavaScript便发挥作用了……

JavaScript与约束验证API

**约束验证API**提供了丰富的表单定制选项,能够增强标准的HTML字段检查。你可以:

  • – 在用户与字段互动或提交表单前暂停验证
  • – 使用自定义样式显示错误信息
  • – 实现仅靠HTML无法完成的自定义验证。这在需要比较两个输入时尤为必要,例如输入电子邮件或电话号码、检查“新密码”与“确认密码”是否一致,或者确保一个日期晚于另一个日期。

**表单验证**

在使用API之前,应通过将表单的`noValidate`属性设置为`true`(相当于添加`novalidate`属性)来禁用默认的验证和错误消息:

const myform = document.getElementById('myform');
myform.noValidate = true;

接着,可以添加事件处理器,例如在表单提交时:

myform.addEventListener('submit', validateForm);

通过`checkValidity()`或`reportValidity()`方法,处理器可以检查整个表单的有效性,当所有输入字段均有效时返回`true`。(区别在于,`checkValidity()`检查是否有任何输入字段受约束验证影响。)

Mozilla文档解释:

每个无效字段上还会触发一个invalid事件。此事件不会冒泡:必须为使用它的每个控件添加处理程序。

// 提交时验证表单
function validateForm(e) {

  const form = e.target;

  if (form.checkValidity()) {

    // 表单有效 - 进行进一步检查

  }
  else {

    // 表单无效 - 取消提交
    e.preventDefault();

  }

};

A valid form could now incur further validation checks. Similarly, an invalid form could have invalid fields highlighted.

字段验证

单个字段具有以下约束验证属性:

  • willValidate:如果元素是约束验证的候选者,则返回true

  • validationMessage:验证消息。如果字段有效,则此值为空字符串。

  • valitity: 一个 ValidityState 对象。当字段有效时,其 valid 属性设置为 true。如果为 false,则以下一个或多个属性将为 true

    ValidityState 描述
    .badInput 浏览器无法理解输入
    .customError 已设置自定义有效性消息
    .patternMismatch 值与指定的 pattern 属性不匹配
    .rangeOverflow 值大于 max 属性
    .rangeUnderflow 值小于 min 属性
    .stepMismatch 值不符合 step 属性规则
    .tooLong 字符串长度超过 maxlength 属性
    .tooShort 字符串长度小于 minlength 属性
    .typeMismatch 值不是有效的电子邮件或 URL
    .valueMissing required 值为空

`

单个字段具有以下约束验证方法:`

  • `
    – `setCustomValidity(message)`: 为无效字段设置错误消息。当字段有效时,必须传递空字符串,否则字段将永远保持无效状态。`
  • `
    – `checkValidity()`: 当输入有效时返回 `true`。`valitity.valid` 属性具有相同功能,但 `checkValidity()` 还会在字段上触发一个 `invalid` 事件,这在某些情况下可能很有用。`

`

`validateForm()` 处理函数可以遍历每个字段,并在必要时将其父元素应用 `invalid` 类:`

function validateForm(e) {
  const form = e.target;
  if (form.checkValidity()) {
    `
- `// 表单有效 - 进行进一步检查`
- `
  }
  else {
    `
- `// 表单无效 - 取消提交`
- `
    e.preventDefault();
    `
- `// 应用无效类`
- `
    Array.from(form.elements).forEach(i => {
      if (i.checkValidity()) {
        `
- `// 字段有效 - 移除类`
- `
        i.parentElement.classList.remove('invalid');
      }
      else {
        `
- `// 字段无效 - 添加类`
- `
        i.parentElement.classList.add('invalid');
      }
    });
  }
};

`

假设你的 HTML 定义了一个电子邮件字段:

<div>
  <label for="email">email</label>
  <input type="email" id="email" name="email" required />
  <p class="help">Please enter a valid email address</p>
</div>

当电子邮件未指定或无效时,脚本会将invalid类应用于<div>。CSS可以在表单提交时显示或隐藏验证消息:

.help { display: none; }
.invalid .help { display: block; }
.invalid label, .invalid input, .invalid .help {
  color: red;
  border-color: red;
}

创建自定义表单验证器

以下演示展示了一个示例联系表单,该表单要求提供用户名以及电子邮件地址、电话号码或两者之一:

它使用了一个名为FormValidate的通用表单验证类来实现。创建对象时传递表单元素。可以设置第二个可选参数:

  • true以在用户与表单交互时验证每个字段
  • false(默认值)以在首次提交后验证所有字段(字段级验证在此之后发生)
// 验证联系表单
const contactForm = new FormValidate(document.getElementById('contact'), false);

.addCustom(field, func)方法定义了自定义验证函数。以下代码确保emailtel字段有效(两者均没有required属性):

// 自定义验证 - 电子邮件和/或电话
const
  email = document.getElementById('email'),
  tel = document.getElementById('tel');

contactForm.addCustom(email, f => f.value || tel.value);
contactForm.addCustom(tel, f => f.value || email.value);

A FormValidate object monitors both of the following:

  • 触发focusout事件,随后检查单个字段
  • 触发表单submit事件,随后检查所有字段

两者都调用.validateField(field)方法,该方法检查字段是否通过标准约束验证。当通过时,分配给该字段的任何自定义验证函数将依次执行。所有函数必须返回true,字段才有效。

无效字段会在其父元素上应用一个invalid类,通过CSS显示红色帮助信息。

最后,当整个表单有效时,对象会调用一个自定义的submit函数:

// 自定义提交
contactForm.submit = e => {
  e.preventDefault();
  alert('Form is valid!\n(open the console)');
  const fd = new FormData(e.target);
  for (const [name, value] of fd.entries()) {
    console.log(name + ': ' + value);
  }
}

或者,你可以使用标准的addEventListener来处理表单的submit事件,因为FormValidate会在表单无效时阻止其他处理器的运行。

表单精炼

表单是所有Web应用程序的基础,开发者花费大量时间来处理用户输入。约束验证得到了良好的支持:浏览器可以处理大多数检查并显示适当的输入选项。

建议:

  • 尽可能使用标准的HTML输入类型。根据需要设置minmaxstepminlengthmaxlengthpatternrequiredinputmodeautocomplete属性。
  • 如有必要,使用少量JavaScript来实现自定义验证和消息。
  • 对于更复杂的字段,逐步增强标准输入。

最后:忘掉Internet Explorer吧!

除非您的客户主要使用IE浏览器,否则无需自行实现回退验证功能。所有HTML5输入字段在IE中均可工作,但可能需要用户付出更多努力。(例如,IE无法检测到输入的无效电子邮件地址。)您仍需在服务器端验证数据,因此可将此作为IE错误检查的基础。

关于HTML表单和约束验证的常见问题解答(FAQs)

HTML表单验证的重要性何在?

HTML表单验证是网页开发的关键环节。它确保用户在表单中输入的数据在发送到服务器之前符合特定标准。这不仅维护了数据完整性,还通过即时反馈用户输入数据的正确性,提升了用户体验。缺乏表单验证可能导致接收错误、不完整甚至恶意数据,进而引发数据损坏、安全漏洞和系统崩溃等问题。

HTML5如何改进表单验证?

HTML5引入了多种新的表单元素和属性,极大简化和增强了表单验证的过程。例如,它新增了电子邮件、URL和数字等输入类型,这些类型能根据输入内容自动进行数据验证。同时,HTML5还引入了required、pattern、min/max等新属性,允许您对输入数据设置多种约束。此外,HTML5内置了一个验证API,使您能通过JavaScript执行自定义验证。

是否可以在不使用JavaScript的情况下进行表单验证?

是的,您可以仅使用HTML5进行基本表单验证。HTML5提供了多种新的输入类型和属性,允许您对输入数据指定各种约束。例如,可以使用`required`属性使字段成为必填项,`pattern`属性强制特定格式,以及`min/max`属性为数值输入设置范围。但对于更复杂的验证,您可能仍需使用JavaScript。

如何在HTML5表单验证中自定义错误消息?

HTML5提供了一个验证API,允许您自定义错误消息。您可以使用`ValidityState`对象的`setCustomValidity`方法来为字段设置自定义错误消息。该方法接受一个字符串参数,当字段验证失败时,该字符串将成为该字段的验证消息。您可以在字段验证失败时触发`invalid`事件时调用此方法。

如何禁用HTML5表单验证?

您可以通过在表单元素上添加`novalidate`属性来禁用HTML5表单验证。当此属性存在时,浏览器在提交表单时不会执行任何验证。如果您想完全在服务器端处理验证或使用自定义JavaScript,这会很有用。

如何在HTML5中一起验证多个字段?

HTML5本身并不提供对多个字段一起进行验证的内置方法。但你可以通过JavaScript来实现这一功能。你可以编写一个自定义验证函数,检查多个字段的值,并在它们不符合所需条件时设置一个自定义的有效性消息。这个函数可以在表单的提交事件或字段的输入/更改事件响应时被调用。

如何在HTML5中根据另一个字段的值来验证一个字段?

HTML5没有提供基于另一个字段值验证字段的内置方法。但你可以使用JavaScript来实现这一点。你可以编写一个自定义验证函数,检查一个字段的值是否与另一个字段的值相匹配,并在它们不匹配时设置一个自定义的有效性消息。这个函数可以在字段的输入/更改事件响应时被调用。

如何在HTML5中执行异步验证?

HTML5本身并不支持开箱即用的异步验证。不过,你可以通过JavaScript来实现。你可以编写一个自定义验证函数,执行异步操作,如AJAX请求,并根据结果设置一个自定义的有效性消息。这个函数可以在字段的输入/更改事件或表单的提交事件响应时被调用。

如何在HTML5表单验证中样式化错误消息?

HTML5表单验证中的错误消息外观由浏览器决定,无法直接使用CSS进行样式设置。不过,你可以通过JavaScript创建自定义错误消息,并随心所欲地对其进行样式设计。利用验证API可以在字段无效时判断出来,并相应地显示自定义错误消息。

如何测试HTML表单的验证功能?

你可以通过向表单字段输入不同类型的数据并尝试提交表单来测试验证功能。应同时测试有效和无效数据,确保在所有情况下验证都能正确工作。此外,还可以利用自动化测试工具或库进行更全面的测试。

Source:
https://www.sitepoint.com/html-forms-constraint-validation-complete-guide/