表单

大多数网站和应用都包含网络表单。笑话网站(例如 DoWebsitesNeedToLook 完全相同EveryBrowser.com 中)可能没有表单,但起源于愚人节笑话的机器学习Workshop.com (MLW) 也有表单,尽管它是假的。MLW 的主要“号召性用语”是供机器注册研讨会的注册表单。此表单包含在 <form> 元素中。

HTML <form> 元素用于标识文档地标,其中包含用于提交信息的交互式控件。嵌套在 <form> 中,您可以找到构成表单的所有交互式(和非交互式)表单控件。

HTML 的功能非常强大。本部分重点介绍 HTML 的强大功能,并介绍了在不添加 JavaScript 的情况下 HTML 的功能。 使用表单数据客户端以某种方式更新界面通常涉及 CSS 或 JavaScript,此处未作讨论。 我们提供完整的学习表单课程。我们在这里不作重复说明,而是介绍几个表单控件以及支持这些控件的 HTML 属性。

借助表单,您可以让用户能够与您的网站或应用互动、验证输入的信息,以及将数据提交到服务器。通过 HTML 属性,可以要求用户选择表单控件或输入一个值。HTML 属性可以定义特定条件,值必须匹配才有效。当用户尝试提交表单时,所有表单控件值都会经过客户端约束验证,并且可以阻止提交,直到数据符合要求的条件;所有这些值都不需要 JavaScript。您也可以停用此功能:为 <form> 设置 novalidate 属性,或者在按钮上设置 formnovalidate(更为频繁地),保存表单数据供日后填写,从而阻止验证。

提交表单

当用户激活表单内嵌套的提交按钮时,即提交表单。为按钮使用 <input> 时,“值”是按钮的标签,并显示在按钮中。使用 <button> 时,标签是起始和结束 <button> 标记之间的文本。提交按钮可以通过以下两种方式编写:

<input type="submit" value="Submit Form">
<button type="submit">Submit Form</button>

对于一个非常简单的表单,您需要一个 <form> 元素(其中包含一些表单输入)和一个提交按钮。不过,提交表单还有更多作用。

<form> 元素的属性用于设置提交表单所用的 HTTP 方法以及处理表单提交的网址。是的,用户可以提交和处理表单,而且无需任何 JavaScript 即可加载新页面。<form> 元素就是如此强大。

<form> 元素的 actionmethod 属性值分别定义处理表单数据的网址和用于提交数据的 HTTP 方法。默认情况下,表单数据会发送到当前页面。否则,请将 action 属性设置为应发送数据的网址。

发送的数据由表单中各个表单控件的名称/值对组成。默认情况下,这包括嵌套在表单中包含 name 的所有表单控件。不过,借助 form 属性,可以在 <form> 之外包含表单控件,并省略 <form> 中嵌套的表单控件。受表单控件和 <fieldset> 支持,form 属性将与其关联的控件形式的 id 作为其值,而不一定是其嵌套的形式。这意味着,表单控件不需要实际嵌套在 <form> 中。

method 属性定义请求的 HTTP 协议:通常为 GETPOST。如果使用 GET,表单数据将作为 name=value 对的参数字符串发送,并附加到 action 的网址。

使用 POST 时,数据会附加到 HTTP 请求的正文中。在发送安全数据(如密码和信用卡信息)时,请始终使用 POST

还有一个 DIALOG 方法。如果 <form method="dialog"><dialog> 内,提交表单将关闭对话框;有提交事件,但数据既未被清除,也未被提交。同样,不使用 JavaScript。相关内容请参阅对话框部分。请注意,由于此操作不会提交表单,您可能需要在提交按钮上同时添加 formmethod="dialog"formnovalidate

表单按钮可以包含本部分开头所述的属性。如果按钮包含 formactionformenctypeformmethodformnovalidateformtarget 属性,则在用于激活表单提交功能的按钮上设置的值优先于在 <form> 上设置的 actionenctypemethodtarget。限制条件验证会在表单提交之前进行,但前提是已启用的提交按钮上既没有 formnovalidate<form> 上也没有 novalidate

如需捕获用于提交表单的按钮,请为该按钮指定 name。提交表单时,没有名称或值的按钮不会与表单数据一起发送。

提交表单后

当用户提交填好的在线表单后,相关表单控件的名称和值也会一并提交。名称是 name 属性的值。这些值来自 value 属性的内容或用户输入或选择的值。<textarea> 的值是其内部文本。<select> 的值为所选 <option>value;如果 <option> 不包含 value 属性,则值为所选选项的内部文本。

<form method="GET">
  <label for="student">Pick a student:</label>
  <select name="student" id="student">
    <option value="hoover">Hoover Sukhdeep</option>
    <option>Blendan Smooth</option>
    <option value="toasty">Toasty McToastface</option>
  </select>
  <input type="submit" value="Submit Form">
</form>

选择“Hoover Sukhdeep”(或不执行任何操作,因为浏览器显示并默认选择第一个选项值),然后点击提交按钮会重新加载此页面,并将网址设置为:

https://web.dev/learn/html/forms?student=hoover

由于第二个选项没有 value 属性,因此系统会提交内部文本作为值。选择“Blendan Smooth”并点击提交按钮,系统就会重新加载此页面,并将网址设置为:

https://web.dev/learn/html/forms?student=Blendan+Smooth

提交表单时,发送的信息包括具有 name(未选中复选框和未选中的单选按钮除外)的所有已命名表单控件的名称和值,以及提交表单按钮以外的任何其他按钮的名称和值。对于所有其他表单控件,如果表单控件有名称,但没有输入值或未输入任何值,则系统会提交表单控件的 name 并空值。

输入类型有 22 种,因此我们不能全部涵盖。请注意,在希望用户输入信息时,添加值是可选的,这通常不是一个好主意。 对于用户无法修改该值的 <input> 元素,您应该始终包含一个值,对于类型为 hiddenradiocheckboxsubmitbuttonreset 的输入元素也是如此。

为表单控件使用唯一的 name 可以简化服务器端数据处理,我们建议您这样做,复选框和单选按钮则不受此规则限制。

单选按钮

如果您曾注意到,当您选择一组单选按钮中的单选按钮时,一次只能选择一个单选按钮,这是由 name 属性造成的。通过为组中的每个单选按钮指定相同的 name 来创建这种“只能选择一种”的效果。

name 对该群组而言应该是唯一的:如果您不小心将同一 name 用于两个不同的组,选择第二个组中的单选按钮将取消选择第一个组中具有相同 name 所做的任何选择。

name 以及所选单选按钮的 value 会随表单一起提交。确保每个单选按钮都有相关(且通常是唯一的)value。未选中的单选按钮的值不会发送。

您可以根据需要在一个页面上添加任意数量的单选按钮组,每个组独立运行,前提是每个单选按钮组具有对组内唯一的 name

如果您想在加载网页时选中同名组中的某个单选按钮,请添加 checked 属性。 此单选按钮将与 :default CSS 伪类匹配,即使用户选择其他单选按钮也是如此。当前选择的单选按钮与 :checked 伪类匹配。

如果用户需要从一组单选按钮中选择一个单选按钮,请为至少一个控件添加 required 属性。在组中的单选按钮上包含 required 意味着必须选择才能提交表单,但不一定是具有被选中属性才有效的单选按钮。此外,在 <legend> 中明确指出需要表单控件。单选按钮组以及各个按钮的标记将在后面介绍。

复选框

一个组中的所有复选框都必须具有相同的 name。只有选中的复选框才会随表单提交其 namevalue。如果您选中了多个具有相同名称的复选框,提交时将使用不同的值(可能是的话)。如果您有多个同名的表单控件,那么即使这些控件并非全部复选框,这些控件也会全部提交(使用“&”符号分隔)。

如果您没有在复选框中添加 value,则所选复选框的值将默认为 on,这样可能没有用。如果您有三个名为 chk 的复选框,并且这些复选框均已选中,则系统不会解密提交的表单:

https://web.dev/learn/html/forms?chk=on&chk=on&chk=on

如需将复选框设置为必需,请添加 required 属性。当必须勾选复选框或需要任何表单控件时,始终通知用户。为复选框添加 required 只是将该复选框设为必需复选框;不会影响其他同名复选框。

标签和字段集

为了让用户知道如何填写表单,表单必须可供访问。每个表单控件都必须有一个标签。您还希望为表单控件组添加标签。各个输入区域、选定区域和文本区域使用 <label> 进行标记,而表单控件组则由对它们进行分组的 <fieldset><legend> 的内容进行标记。

在前面的示例中,您可能已经注意到,除提交按钮以外的每个表单控件都有一个 <label>。标签可为表单控件提供可访问的名称。Button 从其内容或值中获取其无障碍名称。所有其他表单控件都需要相关联的 <label>。如果没有关联的标签,浏览器仍会呈现您的表单控件,但用户不知道需要哪些信息。

如需将表单控件与 <label> 明确关联,请在 <label> 中添加 for 属性:该值是与其关联的表单控件的 id

<label for="full_name">Your name</label>
<input type="text" id="full_name" name="name">

将标签与表单控件相关联有诸多好处。标签可以为屏幕阅读器用户提供具有无障碍名称的表单控件,从而方便屏幕阅读器用户访问表单控件。标签也属于“命中区域”;标签通过增加该区域,使存在灵敏性问题的用户更容易使用网站。如果您使用鼠标,请尝试点击“您的姓名”标签上的任意位置。这样做可让输入焦点。

如需提供隐式标签,请在开始和结束 <label> 标记之间添加表单控件。从屏幕阅读器和指针设备的角度来看,这同样可以进行访问,但不像显式标签那样提供样式钩子。

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

由于标签属于“命中区域”,因此不要在显式标签内添加互动元素,也不要在隐式标签中包含已加标签的表单控件以外的任何其他互动组件。例如,如果您在标签中包含一个链接,而浏览器会呈现 HTML,如果用户点击该标签以进入表单控件,却被重定向到新页面,他们会感到困惑。

一般情况下,<label> 位于表单控件之前,单选按钮和复选框除外。此条件不是必需的。 这只是常见的用户体验模式。学习表单系列内容包含有关表单设计的信息

对于单选按钮和复选框组,标签为其关联的表单控件提供可访问的名称;但控件组及其标签也需要标签。如需为组添加标签,请将所有元素分组到一个 <fieldset> 中,其中 <legend> 为组提供标签。

<fieldset>
  <legend>Who is your favorite student?</legend>
  <ul>
    <li>
      <label>
        <input type="radio" value="blendan" name="machine"> Blendan Smooth
      </label>
    </li>
    <li>
      <label>
        <input type="radio" value="hoover" name="machine"> Hoover Sukhdeep
      </label>
    </li>
    <li>
      <label>
        <input type="radio" value="toasty" name="machine"> Toasty McToastface
      </label>
    </li>
  </ul>
</fieldset>

在此示例中,隐式 <label> 会为每个单选按钮添加标签,而 <legend> 为单选按钮组提供标签。将一个 <fieldset> 嵌套在另一个 <fieldset> 中是标准做法。例如,如果一个表单包含许多问题的调查问卷,分成若干组相关问题,则“最喜欢的学生”<fieldset> 可能会嵌套在另一个标记为“您的收藏”的 <fieldset> 中:

<fieldset>
  <legend>Your favorites:</legend>
  <ul start="6">
    <li>
      <fieldset>
        <legend>Who is your favorite student?</legend>
        <ul>
          <li>
            <!-- the rest of the code here -->

这些元素的默认外观导致其使用不足,但 <legend><fieldset> 可以使用 CSS 来设置样式。 除了所有全局属性之外,<fieldset> 还支持 namedisabledform 属性。如果您停用某个字段集,所有嵌套的表单控件都会被停用。nameform 属性在 <fieldset> 上用得不多。name 可用于通过 JavaScript 访问字段集,但字段集本身不会包含在提交的数据中(包含嵌套在其中的命名表单控件)。

输入类型和动态键盘

如前所述,有 22 种不同类型的输入。在某些情况下,如果用户使用的设备(例如手机)仅在需要时显示动态键盘,则显示的键盘类型由所用的输入类型决定。显示的默认键盘可针对所需的输入类型进行优化。例如,输入 tel 会显示一个针对电话号码输入进行了优化的键盘;email 包含 @.url 的动态键盘包含冒号和斜杠符号。遗憾的是,iPhone 仍未在 url 输入类型的默认动态键盘中包含 :

iPhone 和两款不同 Android 手机上的 <input type="tel"> 键盘:

iPhone 键盘,显示 input type=tel。 Android 键盘,显示 input type=tel。 Android 键盘,显示 input type=tel。

iPhone 和两款不同 Android 手机上的 <input type="email"> 键盘:

iPhone 键盘,显示 input type=email。 Android 键盘,显示 input type=email。 Android 键盘,显示 input type=email。

使用麦克风和摄像头

文件输入类型 <input type="file"> 支持通过表单上传文件。文件可以是任意类型,由 accept 属性定义和限制。可接受的文件类型列表可以是文件扩展名的逗号分隔列表、全局类型或全局类型和扩展名的组合。例如,accept="video/*, .gif" 接受任何视频文件或 GIF 动画。对声音文件使用“audio/*”,对视频文件使用“video/*”,对图片文件使用“image/*”。

如果要使用用户的摄像头或麦克风创建新的媒体文件,可以使用媒体拍摄规范中定义的枚举 capture 属性。对于朝向用户的输入设备,您可以将该值设置为 user;对于手机的后置摄像头或麦克风,您可以将该值设置为 environment。通常,使用 capture(不指定值)之所以可行,是因为用户要选择自己要使用的输入设备。

<label for="avatar">A recent photo of yourself:</label>
<input type="file" capture="user" accept="image/*" name="avatar" id="avatar">

内置验证

同样,由于不含任何 JavaScript,HTML 可能会阻止提交包含无效值的表单。

有些 CSS 选择器会根据是否存在 HTML 属性来匹配表单控件,包括::required:optional(如果布尔值 required 已设置);:default(如果 checked 为硬编码);:enabled:disabled(如果 :disabled 存在互动,具体取决于相应元素是否具有互动性,以及该元素是否具有互动性):disableddisabled:read-write 伪类会匹配设置了 contenteditable 的元素以及默认可修改的表单控件,如 numberpasswordtext 输入类型(但不包括复选框、单选按钮或 hidden 类型等)。如果正常可写元素设置了 readonly 属性,则它会改为匹配 :read-only

当用户在表单控件中输入信息时,CSS 界面选择器(包括 :valid:invalid:in-range:out-of-range)将根据状态开启和关闭。当用户退出表单控件时,尚不完全受支持的 :user-invalid:user-valid 伪类将会匹配。

在用户与表单互动时,您可以使用 CSS 来提供相关提示,说明表单控件是否是必需的且是否有效。 您甚至可以使用 CSS 来阻止用户在表单生效前点击“提交”按钮:

form:invalid [type="submit"] {
  opacity: 50%;
  pointer-events: none;
}

此 CSS 代码段是一种反模式。虽然界面可能让人感觉直观明了,但许多用户会尝试提交表单以启用错误消息功能。以这种方式将提交按钮显示为停用状态不允许进行约束验证,而许多用户都依赖这项功能。

已应用的 CSS 会根据界面的当前状态持续更新。例如,当您添加具有 emailnumberurl 和日期类型等限制条件的输入类型时,如果值为非 null(不为空)且当前值不是有效的电子邮件、数字、网址、日期或时间,那么 :invalid CSS 伪类将匹配。此常量更新与内置 HTML 约束验证不同,内置 HTML 约束验证仅在用户尝试提交表单时发生。

内置限制条件验证仅与使用 HTML 属性设置的限制条件相关。虽然您可以根据 :required:valid/:invalid 伪类来设置元素的样式,但浏览器会在提交表单时提供源自错误(基于 requiredpatternminmax 甚至 type 属性)的错误消息。

一条错误消息,指明必须填写多选字段。

当我们在未选择所需学生的情况下尝试提交表单时,约束验证会因 validityState.valueMissing 错误而无法提交表单。

如果任意一个 validityState 属性返回 true,提交就会被阻止,并且浏览器会在第一个不正确的表单控件中显示错误消息,从而聚焦于该控件。当用户激活表单提交且存在无效值时,第一个无效表单控件将显示错误消息并接收焦点。如果所需的控件未设置值、数值超出范围,或者值不是 type 属性所要求的类型,则表单不会进行验证,也不会提交,并且系统会显示错误消息。

如果 number、日期或时间值低于所设置的最小 min 或高于所设置的最大 max 值,则控件将为 :out-of-range(以及 :invalid),并且当用户尝试提交表单时,系统会通知用户 valididityState.rangeUnderflowvalidityState.rangeOverflow 错误。如果该值与 step 值不一致(无论是明确设置还是默认为 1),则控件将为 :out-of-range(以及 :invalid),并且会发生 validityState.stepMismatch 错误。错误会显示为一个气泡,并且默认情况下会提供有关如何更正错误的实用信息。

值的长度也具有相似的属性:minlengthmaxlength 属性会在提交时通过 validityState.tooLongvalidityState.tooShort 提醒用户存在错误。maxlength 还可以防止用户输入过多字符。

使用 maxlength 属性可能会导致用户体验不佳。一般来说,如果允许用户输入长度超过允许的字符长度,最好提供计数器;可以选择采用未随表单提交的 <output> 元素的形式,这样用户就可以向下修改文本,直到输出显示不超过允许的长度上限。maxlength 可以包含在您的 HTML 中;就像我们讨论过的所有内容一样,它可以在没有 JavaScript 的情况下运行。然后,在加载时,maxlength 属性的值可用于在 JavaScript 中创建此字符计数器。

某些输入类型似乎具有默认约束条件,但实际上没有。例如,tel 输入类型在带有动态键盘的设备上提供数字电话拨号键盘,但不限制有效值。对于此输入类型和其他输入类型,可以使用 pattern 属性。您可以指定正则表达式,值需要匹配才会被视为有效。如果某个值是空字符串,且该值不是必需的,则不会导致 validityState.patternMismatch 错误。如果需要为空,系统将向用户显示 validityState.valueMissing 的默认错误消息,而不是 patternMismatch

对于电子邮件,validityState.typeMismatch 可能无法满足您的需求。您可能需要添加 pattern 属性,以免没有 TLD 的内网电子邮件地址被视为有效地址。模式属性支持提供值必须匹配的正则表达式。要求进行模式匹配时,请确保用户非常清楚自己需要做什么。

所有这些操作都无需一行 JavaScript 即可实现,但作为 HTML API,您可以使用 JavaScript 在限制条件验证期间添加自定义消息。您还可以使用 JavaScript 更新剩余字符数、显示密码强度进度条,或采用任何其他方式来动态提高自动补全功能

示例

此示例在 <dialog> 中有一个表单,其中包含一个嵌套的 <form>,其中包含三个表单控件和两个提交按钮,以及明确的标签和说明。

第一个提交按钮会关闭对话框。使用 formmethod="dialog" 替换表单的默认方法,并关闭 <dialog> 而不提交或清除数据。您还必须添加 formnovalidate,否则浏览器将尝试验证所有必填字段是否都有值。用户可能想要关闭对话框和表单,但不输入任何数据;验证会阻止这种情况。添加 aria-label="close",因为“X”是已知的视觉提示,但不是描述性标签。

表单控件都具有隐式标签,因此您无需添加 idfor 属性。这两个输入元素都具有必需属性,使其是必需的。数字输入框已明确设置 step,以演示 step 是如何包含的。由于 step 默认为 1,因此此属性可以省略。

<select> 具有默认值,因此无需 required 属性。该值默认为内部文本,而不是在每个选项中添加 value 属性。

最后的提交按钮将表单方法设置为 POST。点击后,系统会检查每个值的有效性。如果所有值均有效,系统会提交表单数据并关闭对话框,并且页面可能会重定向到 thankyou.php,即操作网址。如果缺少任何值,或者数值对应的步骤不匹配或超出范围,系统将显示相关浏览器定义的错误消息,表单无法提交,对话框也不会关闭。您可以使用 validityState.setCustomValidity('message here') 方法自定义默认错误消息。请注意,如果您设置了自定义消息,则必须在所有内容均有效时将该消息明确设置为空字符串,否则表单将无法提交。

其他注意事项

其中有一整块区域专门用于帮助用户在表单中输入正确的数据。为了提供良好的用户体验,请务必提供说明并提供必要的提示,以免用户犯错。虽然本部分只介绍了 HTML 如何单独提供验证客户端,但验证必须同时在客户端和服务器端进行验证。在表单填写期间,验证可以以不显眼的方式提供,例如在值正确时添加对勾标记。不过,请勿在表单控件完成之前提供错误消息。如果用户确实犯错了,请告知用户错误在哪里以及哪里出错了。

设计表单时,请务必考虑到并非所有人都像您一样。某人可能用一个字母作为姓氏(或者根本没有姓氏),可能没有邮政编码,可能有三行街道地址,也可能没有街道地址。对方可能在查看表单的翻译版本。

表单控件、其标签和错误消息应显示在屏幕上,准确且有意义、可程序化确定,并以程序化方式与相应的表单元素或组相关联。autocomplete 属性可以而且应该用于实现更快的表单填写速度并改进无障碍功能。

HTML 提供了所有工具,可用于使基本表单控件可供访问。表单元素或流程的交互性越强,就应该更加关注焦点管理、设置和更新 ARIA 名称、角色和值(如有必要)以及 ARIA 实时通知(根据需要)。但是,正如我们在这里学到的那样,仅使用 HTML,您无需借助 ARIA 或 JavaScript 就能远距离实现可访问性和有效性的目标。

检查您的掌握程度

测试您对表单知识的掌握情况。

如何使单选按钮属于同一组?

将它们全都放在一个字段集中。
请重试。
为它们提供相同的 name 属性值。
正确!
为它们提供相同的 id 属性值。
请重试。

哪个 HTML 元素用于告知用户此表单字段的用途?

<h1>
请重试。
<title>
请重试。
<label>
正确!