如何构建 Web 表单
在掌握基本知识的基础上,我们现在将更详细地了解用于为表单的不同部分提供结构和意义的元素。
先决条件 | 对 HTML 的基本了解。 |
---|---|
目标 | 了解如何构建 HTML 表单并为其赋予语义,使其可用且可访问。 |
表单的灵活性使其成为 HTML 中最复杂的结构之一;您可以使用专用表单元素和属性构建任何类型的基本表单。在构建 HTML 表单时使用正确的结构将有助于确保表单既可用又 可访问。
<form> 元素
<form>
元素正式定义了一个表单及其确定表单行为的属性。每次您要创建 HTML 表单时,您都必须使用此元素开始,将所有内容嵌套在其中。许多辅助技术和浏览器插件可以发现 <form>
元素并实施特殊钩子以使其更容易使用。
我们在上一篇文章中已经遇到了这个。
警告:严格禁止将一个表单嵌套在另一个表单中。嵌套会导致表单的行为不可预测,因此这是一个不好的主意。
始终可以使用表单控件在 <form>
元素之外。如果您这样做,默认情况下,该控件与任何表单无关,除非您使用其 form
属性将其与表单关联。这是为了让您显式地将控件绑定到表单,即使它没有嵌套在其中。
让我们继续前进,介绍您将在表单中嵌套的结构元素。
<fieldset> 和 <legend> 元素
<fieldset>
元素是创建具有相同目的的部件组的便捷方法,用于样式和语义目的。您可以通过在打开的 <fieldset>
标记下方包含一个 <legend>
元素来标记 <fieldset>
。 <legend>
的文本内容正式描述了它所包含的 <fieldset>
的目的。
许多辅助技术将 <legend>
元素视为对应 <fieldset>
元素中每个控件标签的一部分。例如,一些屏幕阅读器,例如 Jaws 和 NVDA 将在朗读每个控件的标签之前朗读图例的内容。
这是一个小例子
<form>
<fieldset>
<legend>Fruit juice size</legend>
<p>
<input type="radio" name="size" id="size_1" value="small" />
<label for="size_1">Small</label>
</p>
<p>
<input type="radio" name="size" id="size_2" value="medium" />
<label for="size_2">Medium</label>
</p>
<p>
<input type="radio" name="size" id="size_3" value="large" />
<label for="size_3">Large</label>
</p>
</fieldset>
</form>
注意:您可以在 fieldset-legend.html 中找到此示例 (也请查看在线版本).
在朗读上述表单时,屏幕阅读器将为第一个部件朗读“果汁尺寸小”,为第二个朗读“果汁尺寸中”,为第三个朗读“果汁尺寸大”。
此示例中的用例是最重要的用例之一。每次您有一组单选按钮时,都应将它们嵌套在 <fieldset>
元素中。还有其他用例,一般来说, <fieldset>
元素也可以用来对表单进行分段。理想情况下,长表单应该分布在多个页面上,但如果表单很长且必须在一个页面上,将不同的相关部分放在不同的字段集中可以提高可用性。
由于它对辅助技术的影响, <fieldset>
元素是构建可访问表单的关键元素之一;但是,您有责任不要滥用它。如果可能,每次您构建表单时,都尝试 听听屏幕阅读器 如何解释它。如果听起来很奇怪,请尝试改进表单结构。
<label> 元素
正如我们在上一篇文章中看到的, <label>
元素是正式定义 HTML 表单部件标签的方法。如果您想构建可访问的表单,这是最重要的元素——当正确实现时,屏幕阅读器会朗读表单元素的标签以及任何相关的说明,同时它对有视力的用户也很有用。以这个例子为例,我们在上一篇文章中看到了这个例子
<label for="name">Name:</label> <input type="text" id="name" name="user_name" />
通过 <label>
通过其 for
属性(其中包含 <input>
元素的 id
属性)与 <input>
正确关联,屏幕阅读器将朗读类似“姓名,编辑文本”的内容。
还有一种将表单控件与标签关联的方法——将表单控件嵌套在 <label>
中,隐式关联它。
<label for="name">
Name: <input type="text" id="name" name="user_name" />
</label>
但是,即使在这种情况下,也建议设置 for
属性以确保所有辅助技术都能理解标签和部件之间的关系。
如果没有标签,或者表单控件没有隐式或显式与标签关联,屏幕阅读器将朗读类似“编辑文本空白”的内容,这根本没有帮助。
标签也可以点击!
设置正确的标签的另一个优点是,您可以单击或点击标签以激活相应的部件。这对文本输入等控件很有用,您可以单击标签和输入来聚焦它,但它对单选按钮和复选框特别有用——此类控件的点击区域可能非常小,因此使它尽可能容易激活非常有用。
例如,单击下面示例中的“我喜欢樱桃”标签文本将切换 taste_cherry 复选框的选择状态
<form>
<p>
<input type="checkbox" id="taste_1" name="taste_cherry" value="cherry" />
<label for="taste_1">I like cherry</label>
</p>
<p>
<input type="checkbox" id="taste_2" name="taste_banana" value="banana" />
<label for="taste_2">I like banana</label>
</p>
</form>
注意:您可以在 checkbox-label.html 中找到此示例 (也请查看在线版本).
多个标签
严格来说,您可以在一个部件上放置多个标签,但这并不是一个好主意,因为某些辅助技术可能无法处理它们。在存在多个标签的情况下,您应该将部件及其标签嵌套在一个 <label>
元素中。
让我们考虑这个例子
<p>Required fields are followed by <span aria-label="required">*</span>.</p>
<!-- So this: -->
<!--div>
<label for="username">Name:</label>
<input id="username" type="text" name="username" required>
<label for="username"><span aria-label="required">*</label>
</div-->
<!-- would be better done like this: -->
<!--div>
<label for="username">
<span>Name:</span>
<input id="username" type="text" name="username" required>
<span aria-label="required">*</span>
</label>
</div-->
<!-- But this is probably best: -->
<div>
<label for="username">Name: <span aria-label="required">*</span></label>
<input id="username" type="text" name="username" required />
</div>
顶部的段落陈述了必填元素的规则。该规则必须在使用之前包含,以便有视力的用户和使用屏幕阅读器等辅助技术的用户在遇到必填元素之前了解其含义。虽然这有助于告知用户星号的含义,但不能依赖于它。屏幕阅读器在遇到星号时会将其朗读为“星号”。当有视力的鼠标用户悬停时,应该显示“必填”,这是通过使用 title
属性实现的。标题是否朗读取决于屏幕阅读器的设置,因此更可靠的做法是也包括 aria-label
属性,该属性始终被屏幕阅读器朗读。
上述变体随着您的使用而变得越来越有效
- 在第一个示例中,标签根本没有与输入一起朗读——您只得到“编辑文本空白”,以及单独朗读的实际标签。多个
<label>
元素使屏幕阅读器感到困惑。 - 在第二个示例中,事情变得更加清晰——与输入一起朗读的标签是“姓名星号姓名编辑文本必填”,标签仍然被单独朗读。事情仍然有点混乱,但这次稍微好一些,因为
<input>
与标签相关联。 - 第三个示例是最好的——实际标签被全部朗读,与输入一起朗读的标签是“姓名必填编辑文本”。
注意:您可能会得到略有不同的结果,具体取决于您的屏幕阅读器。这在 VoiceOver 中进行了测试(NVDA 的行为类似)。我们也很乐意听到您的体验。
注意:您可以在 GitHub 上找到此示例,名为 required-labels.html (也请查看在线版本). 不要使用 2 或 3 个版本中的其中一个未注释的版本测试示例——如果您有多个标签和多个具有相同 ID 的输入,屏幕阅读器肯定会感到困惑!
与表单一起使用的常见 HTML 结构
除了特定于 Web 表单的结构之外,还应该记住表单标记只是 HTML。这意味着您可以使用 HTML 的所有功能来构建 Web 表单。
正如您在示例中看到的,将标签及其部件与 <li>
元素一起包装在 <ul>
或 <ol>
列表中是常见的做法。 <p>
和 <div>
元素也经常使用。建议使用列表来构建多个复选框或单选按钮。
除了 <fieldset>
元素之外,使用 HTML 标题(例如 h1, h2)和分段(例如 <section>
)来构建复杂表单也是常见的做法。
最重要的是,您可以找到一种舒适的编码风格,从而创建可访问且可用的表单。每个单独的功能部分都应该包含在一个单独的 <section>
元素中,其中包含 <fieldset>
元素来包含单选按钮。
主动学习:构建表单结构
让我们将这些想法付诸实践,构建一个稍微复杂一些的表单——支付表单。此表单将包含许多您可能还不了解的控件类型。现在不用担心;您将在下一篇文章 (基本原生表单控件) 中了解它们的工作原理。现在,在按照以下说明操作时仔细阅读说明,并开始了解我们正在使用哪些包装器元素来构建表单,以及为什么。
- 首先,在您的计算机上的新目录中,制作我们 空白模板文件 的本地副本。
- 接下来,通过添加
<form>
元素来创建您的表单html<form>
- 在
<form>
元素中,添加标题和段落以告知用户必填字段的标记方式html<h1>Payment form</h1> <p> Required fields are followed by <strong><span aria-label="required">*</span></strong>. </p>
- 接下来,我们将向表单添加一个更大的代码段,放在我们之前条目的下方。在这里,您将看到我们正在将联系信息字段包装在一个单独的
<section>
元素中。此外,我们有一组三个单选按钮,我们每个按钮都放在自己的列表 (<li>
) 元素中。我们还有两个标准文本<input>
及其关联的<label>
元素,每个元素都包含在一个<p>
中,以及一个用于输入密码的密码输入。将此代码添加到您的表单中html<section> <h2>Contact information</h2> <fieldset> <legend>Title</legend> <ul> <li> <label for="title_1"> <input type="radio" id="title_1" name="title" value="A" /> Ace </label> </li> <li> <label for="title_2"> <input type="radio" id="title_2" name="title" value="K" /> King </label> </li> <li> <label for="title_3"> <input type="radio" id="title_3" name="title" value="Q" /> Queen </label> </li> </ul> </fieldset> <p> <label for="name"> <span>Name: </span> <strong><span aria-label="required">*</span></strong> </label> <input type="text" id="name" name="username" required /> </p> <p> <label for="mail"> <span>Email: </span> <strong><span aria-label="required">*</span></strong> </label> <input type="email" id="mail" name="usermail" required /> </p> <p> <label for="pwd"> <span>Password: </span> <strong><span aria-label="required">*</span></strong> </label> <input type="password" id="pwd" name="password" required /> </p> </section>
- 表单的第二个 `<section>` 是支付信息。我们有三个独立的控件及其标签,每个都包含在 `<p>` 内。第一个是用于选择信用卡类型的下拉菜单 (
<select>
)。第二个是类型为 `tel` 的 `<input>` 元素,用于输入信用卡号码;虽然我们可以使用 `number` 类型,但我们不想要数字的旋转器 UI。最后一个是类型为 `text` 的 `<input>` 元素,用于输入卡的有效期;这包括一个表示正确格式的 *占位符* 属性,以及一个测试输入日期是否具有正确格式的 *模式*。这些较新的输入类型在 HTML5 输入类型 中重新介绍。在上一节下面输入以下内容html<section> <h2>Payment information</h2> <p> <label for="card"> <span>Card type:</span> </label> <select id="card" name="usercard"> <option value="visa">Visa</option> <option value="mc">Mastercard</option> <option value="amex">American Express</option> </select> </p> <p> <label for="number"> <span>Card number:</span> <strong><span aria-label="required">*</span></strong> </label> <input type="tel" id="number" name="cardnumber" required /> </p> <p> <label for="expiration"> <span>Expiration date:</span> <strong><span aria-label="required">*</span></strong> </label> <input type="text" id="expiration" name="expiration" required placeholder="MM/YY" pattern="^(0[1-9]|1[0-2])\/([0-9]{2})$" /> </p> </section>
- 我们将添加的最后一个部分要简单得多,只包含一个
<button>
,类型为 `submit`,用于提交表单数据。现在将此添加到表单的底部html<section> <p> <button type="submit">Validate the payment</button> </p> </section>
- 最后,通过添加外部
<form>
结束标签来完成表单html</form>
我们对下面完成的表单应用了一些额外的 CSS。如果您想更改表单的外观,可以从 示例 中复制样式,或者访问 样式化 Web 表单。
测试您的技能!
您已经到达本文的结尾,但您能记住最重要的信息吗?在继续之前,您可以找到一个进一步的测试来验证您是否保留了这些信息——请参见 测试您的技能:表单结构。
总结
您现在拥有了正确构建 Web 表单所需的所有知识。我们将在接下来的几篇文章中介绍这里介绍的许多功能,下一篇文章将更详细地介绍如何使用所有不同类型的表单小部件来从您的用户那里收集信息。