表单

大多数网站和应用都包含网络表单。笑话网站(例如 DoWebsitesNeedToLookExactlyTheSameInEveryBrowser.com)可能没有表单,但即使是起源于愚人节笑话的 MachineLearningWorkshop.com (MLW) 也有表单,尽管是虚假表单。MLW 的主要“号召性用语”是机器注册表单,供机器注册参加研讨会。此表单包含在 <form> 元素中。

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

HTML 功能强大。本部分重点介绍 HTML 的强大功能,介绍 HTML 在不添加 JavaScript 的情况下可实现的功能。 在客户端使用表单数据以某种方式更新界面通常涉及 CSS 或 JavaScript,本文不会对此进行讨论。您可以完整学习“了解表单”课程。我们不会在此一一列出,但会介绍几个表单控件以及支持这些控件的 HTML 属性。

借助表单,您可以让用户与您的网站或应用互动、验证输入的信息,并将数据提交到服务器。HTML 属性可以让您要求用户选择表单控件或输入值。HTML 属性可以定义值必须符合的特定条件,以便其有效。当用户尝试提交表单时,所有表单控件值都会经过客户端约束条件验证,并且在数据与所需条件匹配之前,系统会阻止提交;所有这些都无需 JavaScript。您还可以关闭此功能:在 <form> 上设置 novalidate 属性(更常见的是,在按钮上设置 formnovalidate),保存表单数据以供日后完成,从而防止验证。

提交表单

当用户激活表单中嵌套的提交按钮时,表单会提交。将 <input> 用于按钮时,“value”是按钮的标签,并显示在按钮中。使用 <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 只会使该复选框变为必填;不会影响其他同名复选框。

标签和 fieldset

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

在前面的示例中,您可能已经注意到,除了提交按钮之外,每个表单控件都有 <label>。标签可为表单控件提供无障碍名称。按钮会根据其内容或值获取可访问名称。所有其他表单控件都需要关联的 <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 -->

这些元素的默认外观导致它们被过度使用,但您可以使用 CSS 为 <legend><fieldset> 设置样式。除了所有全局属性之外,<fieldset> 还支持 namedisabledform 属性。停用 fieldset 后,所有嵌套的表单控件都会被停用。nameform 属性在 <fieldset> 中用处不大。name 可用于使用 JavaScript 访问 fieldset,但 fieldset 本身不会包含在提交的数据中(其中嵌套的命名表单控件会包含在内)。

输入类型和动态键盘

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

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

显示输入类型为“tel”的 iPhone 键盘。 显示输入类型为“tel”的 Android 键盘。 显示输入类型为“tel”的 Android 键盘。

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

显示“input type=email”的 iPhone 键盘。 显示输入类型为“电子邮件”的 Android 键盘。 显示“input type=email”的 Android 键盘。

访问麦克风和摄像头

文件输入类型 <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 属性)。: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 的内部电子邮件地址不被视为有效。借助 pattern 属性,您可以提供值必须匹配的正则表达式。当需要进行模式匹配时,请确保向用户明确说明预期结果。

您无需编写一行 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。

检查您的理解情况

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

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

将它们全部放入 fieldset 中。
请重试。
为它们指定相同的 name 属性值。
正确!
为它们提供所有相同的 id 属性值。
请重试。

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

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