HTML 中的表单和按钮
HTML 表单和按钮是与网站用户交互的强大工具。最常见的情况是,它们为用户提供控件来操作用户界面 (UI) 或在需要时输入数据。
在本文中,我们将介绍表单和按钮的基础知识。需要了解的内容还有很多——很多输入类型和表单功能都没有提及——但本文将为您在大多数情况下提供坚实的基础。您可以根据需要在职业生涯中不断学习,了解高级或专业用途。
| 预备知识 | 基本的 HTML 熟悉程度,如基本 HTML 语法中所述。文本级语义,例如标题和段落以及列表。结构化 HTML。 |
|---|---|
| 学习成果 |
|
与用户交互
到目前为止,在本课程中,您已经看到用户与 Web 交互的几种方式
- 链接可用于导航到内容的不同部分,无论是在同一页面还是不同页面。
<video>和<audio>元素通常具有播放/暂停、快进、快退等控件,允许用户根据自己的意愿消费媒体内容。
然而,这些功能往往促进单向交互,用户被动地消费内容。这很好,但网络是一种双向体验。网站用户设置他们希望体验内容和服务的方式。他们订出租车并请求回电。他们提供反馈并提出投诉。他们购买产品并将其送到家中。
要提供这种双向体验,您需要使用按钮和表单。
按钮通常使用 HTML <button> 元素创建(它们有时也使用 <input> 元素创建,其 type 属性设置为 button 或 submit 之类的值)。这些按钮是通用用途的——您可以将它们连接起来以触发您想要的任何功能,仅受您的想象力和编码技能的限制。
表单使用 <form>、<label>、<input> 和 <select> 等元素创建。表单元素可以创建比简单按钮更复杂的控件——例如,一个包含多个选项的下拉菜单,允许您选择用户界面的不同主题。
然而,至关重要的是,当用户需要向网站服务器提交信息时,它们也可以用于创建表单供用户填写。想想电子商务网站——当您想搜索要购买的产品时,您可以使用表单输入搜索词。当您想支付一些商品并完成配送时,您可以使用表单输入您的邮寄地址,并使用另一个表单输入您的信用卡详细信息。
在本文中,我们将主要关注这种更传统的表单元素使用方式。请注意,按钮也常用于表单内部,以将输入的数据提交到服务器。
有了这些重要的理论知识,让我们继续探索代码,看看按钮和表单是如何实现的。
按钮
如上所述,按钮在 Web 上有几个主要用途。首先,它们用于触发功能,这在创建 UI 控件时很有用。最简单的按钮使用以下代码实现
<button>Press me</button>
它呈现如下
<button></button> 标签之间出现的文本呈现在按钮内部,并且浏览器会给它一些基本样式,因此它默认会看起来和行为都像一个按钮。到目前为止,一切顺利。然而,这里有一个问题——我们孤独的按钮本身不会做任何有用的事情。要使它做一些有用的事情,您需要将它放在一个表单中(我们稍后会介绍),或者添加一些 JavaScript。
例如,如果您将以下 JavaScript 应用于上述按钮
const btn = document.querySelector("button");
btn.addEventListener("click", () => {
btn.textContent = "YOU CLICKED ME!! ❤️";
setTimeout(() => {
btn.textContent = "Press me";
}, 1000);
});
它会给你以下输出——尝试点击它
您现在不需要理解 JavaScript 的工作原理。您将在本课程的后面部分学习更多关于它的知识。
在下一节中,您将看到按钮的第二个主要用途的演示——提交表单。
表单的结构
一个基本表单包含三件事
- 一个
<form>元素,它封装了所有其他表单内容。<form></form>标签内的任何表单控件都属于同一表单,并且它们的数据在表单提交时会被包含在内。 - 一个或多个由
<label>元素和表单控件元素(通常是<input>元素,但也有其他类型,例如<select>)组成的对- 表单控件元素允许用户选择或输入一些数据,这些数据将在表单提交时发送到服务器。
<label>元素提供了一个与表单控件相关联的标识标签,描述了应该输入到其中的数据。
- 一个
<button>元素,用于提交表单。
让我们看一个包含上述三项的基本示例。这个表单可以用来询问用户的姓名和电子邮件,以让他们注册时事通讯(别担心——它没有连接到任何服务器,所以目前不会做任何事情)。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>First form</title>
</head>
<body>
<form action="./submit_page" method="get">
<h2>Subscribe to our newsletter</h2>
<p>
<label for="name">Name (required):</label>
<input type="text" name="name" id="name" required />
</p>
<p>
<label for="email">Email (required):</label>
<input type="email" name="email" id="email" required />
</p>
<p>
<button>Sign me up!</button>
</p>
</form>
</body>
</html>
它呈现如下
如果您立即点击“注册我!”,您会看到一个验证错误,因为没有输入数据。如果您填写了姓名和电子邮件地址,然后点击“注册我!”,您会看到一个 404 错误消息。
我们稍后会解释原因。在继续之前,使用您的代码编辑器将前面的 HTML 代码列表复制到一个新的 HTML 文件中,并在新的浏览器选项卡中打开它。
<form> 元素
正如我们之前所说,<form> 元素充当表单的外部包装器,将其中所有表单控件组合在一起。当 <button> 被按下时,所有由表单控件表示的数据都将提交到服务器。<form> 元素可以接受许多属性,但我们示例中包含的两个最重要的属性如下
action: 包含要将提交的表单数据发送到并进行处理的页面的路径。稍后,在您提交表单后,您会在 URL 中看到/submit_page。您还会收到404错误响应,因为该页面实际上不存在,但目前这没关系。method: 指定要用于将表单数据发送到服务器的数据传输方法。现在不要太担心这个;get值会导致数据作为参数附加到 URL 的末尾发送。
检查提交的数据
- 转到单独选项卡中的示例,尝试输入“Bob”作为姓名和“bob@bob.com”作为电子邮件地址。
- 按下
<button>。
action 和 method 属性导致表单数据按照以下方式在 URL 中提交
/some/url/submit_page?name=Bob&email=bob%40bob.com
构建表单
您可以在 <form> 元素中包含任何您喜欢的 HTML 元素,以构建表单元素本身,并提供可使用 CSS 进行样式设计的容器等。
在我们的示例中,我们包含了一个标题元素 (<h2>) 来描述表单的目的。
我们还将每个输入/标签对和提交按钮放在一个单独的 <p> 中,这样每个都将出现在单独的行上。这些元素默认都是行内元素,这意味着如果我们不这样做,它们都会在同一行上。
这是表单结构的一种常见模式。有些人使用 <p> 元素来分隔他们的表单元素,有些人使用 <div>、<section>,甚至 <li> 元素。只要使用的元素具有语义意义,这并没有太大关系。例如,将表单元素组分成单独的段落或内容部分,甚至列表中的项目,是说得通的。将它们表示为 blockquote、aside 或 address 则意义不大。
有一个专门用于将表单元素组合在一起的元素,称为 <fieldset>。这在某些情况下很有用,例如在复杂表单中,以及将多个复选框和单选按钮组合在一起时。我们稍后会看几个 <fieldset> 示例。
<input> 元素
<input> 元素表示输入到表单中的不同数据项。让我们研究一下我们基本表单中的一个示例
<input type="text" name="name" id="name" required />
属性如下
type: 指定要创建的表单控件的类型。有许多不同类型的表单控件,从不同类型的简单文本字段到单选按钮、复选框等等。text类型呈现一个基本的文本字段,可以接受任何值。name: 指定数据项的名称。当表单提交时,数据以名称/值对的形式发送。在每种情况下,名称都等于此name属性值,而值等于文本字段中输入的文本。id: 指定一个可用于标识元素的 ID。在这种情况下,它用于将表单控件与其<label>相关联。required: 指定在提交表单之前必须在表单元素中输入一个值。这只应在您需要的输入上设置,而不是在可选字段上设置。
您应该知道,某些输入类型通常不会从字段中输入的文本获取其值。例如,<input type="color"> 渲染一个颜色选择器小部件,您可以从中选择一种颜色,而 <input type="radio"> 渲染一个单选按钮控件,可以被选中或不选中。
对于单选按钮,您通常需要提供一个 value 属性,其中包含如果选择该单选按钮则会提交的值。请注意,您可以在 text 和 color 等输入类型上指定 value 属性——效果是该值在表单字段首次渲染时预先填充。
required 和 value 属性的使用
- 再次,转到您在单独选项卡中加载的示例,尝试在两个字段都未输入任何值的情况下提交表单。您会看到“姓名”字段旁边弹出一个错误消息,提示“请填写此字段”(不同浏览器可能有所不同)。这就是
required属性和浏览器默认的客户端表单验证在起作用。 - 现在尝试在第一个字段中输入一个有效的姓名,但在第二个字段中输入一个无效的电子邮件地址(例如“aaaa”)。这次您会看到“电子邮件”字段旁边弹出一个错误消息,提示“请输入电子邮件地址”。
- 尝试编辑表单,在第一个输入中包含
value="Bob"。当您重新加载代码时,您会看到第一个字段默认输入了“Bob”这个值。
专用文本字段输入
上面的第二个练习提出了一个有趣的问题。第二个输入字段特别期望一个电子邮件地址,并以此验证输入的值。如果您再次查看表单代码,您会明白为什么——第二个 <input> 的 type 为 email。有几种专用的文本字段输入类型旨在处理特定类型的数据——<input type="number">、<input type="password">、<input type="tel"> 等。
点击上面的一些链接,了解这些输入类型的作用。查看我们的 <input> 参考,看看您是否能找到更多专用的文本字段输入类型。
<label> 元素
正如我们上面所说,<label> 元素提供与表单控件相关联的标识标签,描述应输入到其中的数据。您可以在 <label> 元素中放置任何您喜欢的文本内容,但它们应该准确描述相关表单控件期望的数据。关联是通过给表单控件一个 id 属性,然后给 <label> 元素一个 for 属性,其值与控件的 id 相同来创建的。
例如
<label for="name">Name (required):</label>
<input type="text" name="name" id="name" required />
<label> 元素很重要,原因有几个,最值得注意的是
- 当视障用户使用屏幕阅读器帮助他们阅读和与网页内容交互时,当遇到每个控件时,屏幕阅读器将读出相关的标签文本。这使用户更容易理解每个控件中应该输入什么内容。
- 它们使您能够通过单击表单元素的标签文本以及控件来聚焦表单元素。这对于手机用户特别有用,在触摸屏上用手指准确选择表单元素可能很困难。在这种情况下,增大点击区域很有用。
显式和隐式表单标签
您上面看到的表单标签样式称为显式表单标签——控件和标签之间的关联是通过 id 和 for 属性明确建立的。您还可以通过将控件嵌套在标签内部来实现在隐式表单标签,如下所示
<label>
Name (required):
<input type="text" name="name" required />
</label>
嵌套在控件和标签之间建立了隐式关联,您不再需要 id 和 for 属性。
这两种方法都可以,但我们建议使用显式标签方法。这是因为显式关联通常更容易识别和理解,尤其是在您的 HTML 代码变得更复杂时。此外,屏幕阅读器(和其他辅助技术)并不总是正确处理隐式标签。
您可以在 HTML Inputs and Labels: A Love Story, csstricks.com (2021) 中阅读更多关于表单标签最佳实践的信息。
<button> 元素
当 <button> 元素包含在 <form> 元素内部时,其默认行为是提交表单,前提是没有无效数据导致客户端表单验证阻止提交。您在上面使用我们的基本表单示例时已经看到了这种行为。
可以通过 <button> 元素的 type 属性指定其他按钮行为
<button type="submit">明确声明一个按钮应表现为提交按钮。您实际上并不需要声明此项,除非出于某种原因您在<form>中包含其他按钮,并且您希望明确哪个是提交按钮。这种情况非常罕见。<button type="reset">创建一个重置按钮——这会立即删除表单中的所有数据,将其重置为初始状态。不要使用重置按钮——它们在 Web 早期很受欢迎,但通常弊大于利。大多数人都经历过填写长表单,却不小心点击了重置按钮而不是提交按钮,这意味着他们不得不重新开始。<button type="button">创建一个与<form>元素之外的按钮行为相同的按钮。正如我们之前看到的,它们默认什么也不做,需要 JavaScript 才能赋予它们功能。
注意:您还可以使用具有相同 type 值(例如 <input type="submit">、<input type="reset"> 和 <input type="button">)的 <input> 元素来创建上述按钮类型。但是,与它们的 <button> 对应项相比,这些按钮有许多缺点。您应该改用 <button>。
关于可访问性的旁注
我们已经讨论了表单标签对于可访问性的重要性,但我们也想就使用正确语义元素创建表单(例如,使用 <button> 提交表单,而不是使用编程为 <button> 的 <div>)的一般重要性发表一些评论。完全可以使用 CSS 和 JavaScript 的组合来使几乎任何 HTML 元素看起来和行为都像表单元素。开发人员通常出于设计原因这样做——某些表单控件很难设置样式。
但是,当您这样做时,您会让自己和用户的生活变得更艰难。浏览器默认提供了一些 <button> 和表单控件功能,无需 JavaScript 或其他额外代码,以使表单对所有用户更具可用性。
例如
- 语义元素可被屏幕阅读器等辅助技术理解,这些技术将它们的含义传达给无法看到它们的用户。
- 表单控件和按钮默认情况下可使用键盘访问。在前面的示例中,尝试使用 Tab 和 Shift + Tab(称为“tabbing”)在表单元素之间向前和向后移动。
- 还要注意,在表单元素之间切换时,聚焦的元素会用蓝色轮廓(称为焦点轮廓)突出显示。这对于键盘用户了解他们在表单中的当前位置是一个重要功能。
如果您不使用正确的语义元素来实现表单,您将不得不重新实现所有这些功能,否则您的表单元素将不会按用户预期的方式运行,因此会显得有问题。这一切都累积起来。
其他控制类型
您可以使用许多其他控制类型在表单中收集数据。让我们看一个稍微复杂一点的示例,然后我们将对其进行探索和解释。
注意:在此示例中,我们假设用户已经注册并登录,因此无需收集姓名和电子邮件等详细信息。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Second form</title>
</head>
<body>
<form action="./payment_page" method="get">
<h2>Register for the meetup</h2>
<fieldset>
<legend>Choose hotel room type:</legend>
<div>
<input
type="radio"
id="hotelChoice1"
name="hotel"
value="economy"
checked />
<label for="hotelChoice1">Economy (+$0)</label>
<input type="radio" id="hotelChoice2" name="hotel" value="superior" />
<label for="hotelChoice2">Superior (+$50)</label>
<input
type="radio"
id="hotelChoice3"
name="hotel"
value="penthouse"
disabled />
<label for="hotelChoice3">Penthouse (+$150)</label>
</div>
</fieldset>
<fieldset>
<legend>Choose classes to attend:</legend>
<div>
<input type="checkbox" id="yoga" name="yoga" />
<label for="yoga">Yoga (+$10)</label>
<input type="checkbox" id="coffee" name="coffee" />
<label for="coffee">Coffee roasting (+$20)</label>
<input type="checkbox" id="balloon" name="balloon" />
<label for="balloon">Balloon animal art (+$5)</label>
</div>
</fieldset>
<p>
<label for="transport">How are you getting here:</label>
<select name="transport" id="transport">
<option value="">--Please choose an option--</option>
<option value="plane">Plane</option>
<option value="bike">Bike</option>
<option value="walk">Walk</option>
<option value="bus">Bus</option>
<option value="train">Train</option>
<option value="jetPack">Jet pack</option>
</select>
</p>
<p>
<label for="comments">Any other comments:</label>
<textarea id="comments" name="comments" rows="5" cols="33"></textarea>
</p>
<p>
<button>Continue to payment</button>
</p>
</form>
</body>
</html>
它呈现如下
我们建议您在学习接下来的几节时,在单独的浏览器选项卡中打开此示例,在这些节中,我们将依次查看每种控制类型。为此,请使用您的代码编辑器将代码复制到一个 HTML 文件中,并在浏览器选项卡中打开它。
在继续之前,请在本地副本中尝试不同的表单控件并选择一些值。尝试提交表单,看看 URL 中提交的数据是什么样的。
单选按钮
“选择酒店房型”按钮是使用 <input type="radio"> 控件实现的。这些控件呈现为一组按钮控件,其中每次只能选择一个——您不能同时选择多个。它们以老式收音机上的按钮命名,在那里您按下一个按钮,之前选择的按钮就会再次弹出。
我们的示例代码看起来像这样
<fieldset>
<legend>Choose hotel room type:</legend>
<div>
<input
type="radio"
id="hotelChoice1"
name="hotel"
value="economy"
checked />
<label for="hotelChoice1">Economy (+$0)</label>
<input type="radio" id="hotelChoice2" name="hotel" value="superior" />
<label for="hotelChoice2">Superior (+$50)</label>
<input
type="radio"
id="hotelChoice3"
name="hotel"
value="penthouse"
disabled />
<label for="hotelChoice3">Penthouse (+$150)</label>
</div>
</fieldset>
radio 输入类型的工作方式与 text 输入类型大致相同,但有一些区别
- 每组单选按钮的
name属性必须包含相同的值,才能将它们作为一组关联起来。如果它们包含不同的值,它们将有效地是独立的集合,提交时具有不同的值。 - 您必须包含一个
value属性,其中包含每个单选按钮要提交的值。提交的值将是名称/值对,但名称始终相同,例如hotel=economy或hotel=superior。 - 每个单选按钮的
<label>应该描述该特定的值选择,而不是您选择的总体值。提供总体值选择描述的首选方法是将它们包装在<fieldset>中,该元素将<legend>元素作为子元素,其中包含描述。
注意:除了构造和标记表单之外,字段集还有其他用途,例如禁用一整套控件作为一个单一单元。
值得注意的是,我们已将 checked 属性应用于第一个单选按钮——这使得它在页面首次加载时被选中。这意味着始终会选择一个选项,并且您无法在不选择另一个单选按钮的情况下取消选择一个单选按钮。
尝试从第一个单选按钮中删除 checked 属性,保存,然后重新加载,查看其效果。在继续之前将其放回。
禁用表单控件
在单选按钮示例中,您会注意到第三个单选按钮设置了 disabled 属性。这会导致渲染的控件变灰且无法选择。这在许多情况下都很有用,例如某个选项通常可用,但现在不可用。例如,产品可能缺货,或者像我们示例中的情况一样,顶层公寓都已预订满!
您可以在任何表单控件(包括 <button> 元素)上设置 disabled 属性。<fieldset> 元素也可以接受 disabled 属性——这会导致字段集内的每个表单控件都被禁用。
尝试在两个 <fieldset> 元素上设置 disabled 属性,保存,然后重新加载,以查看其效果。在继续之前再次删除它们。
复选框
我们的“要参加的课程”选择器是使用 <input type="checkbox"> 控件实现的。这些控件呈现为一组开/关状态的复选框。与单选按钮不同,您可以同时选择多个。
<fieldset>
<legend>Choose classes to attend:</legend>
<div>
<input type="checkbox" id="yoga" name="yoga" />
<label for="yoga">Yoga (+$10)</label>
<input type="checkbox" id="coffee" name="coffee" />
<label for="coffee">Coffee roasting (+$20)</label>
<input type="checkbox" id="balloon" name="balloon" />
<label for="balloon">Balloon animal art (+$5)</label>
</div>
</fieldset>
从代码片段中可以看出,单选按钮和复选框的实现方式非常相似(它们也可以接受 checked 属性,以便在页面加载时预先选中)。它们的行为也相当相似,不同之处在于单选按钮允许您从多个项目中选择零个或一个项目,而复选框允许您从多个项目中选择零个或多个项目。
主要区别(除了 type 值!)在于每个复选框都有不同的 name 值,并且它们通常不给定 value 属性。从行为上看,这意味着它们表示不同的数据值,而一组单选按钮只表示一个。提交时,如果复选框被选中,则每个值都会以 on 值提交——yoga=on、balloon=on 等。
注意:通过给复选框一个 value 属性,可以更改提交的复选框的值,例如:<input type="checkbox" id="yoga" name="yoga" value="yes" /> 将导致在选中时提交 yoga=yes。但是,这样做意义不大。如果选中,复选框要么提交一个单一值,要么根本不提交。发送到服务器的值是什么并不重要。
下拉菜单
下拉菜单,例如我们示例中的“您如何到达这里”选择控件,不是使用 <input> 类型实现的,而是使用 <select> 和 <option> 元素实现的
<label for="transport">How are you getting here:</label>
<select name="transport" id="transport">
<option value="">--Please choose an option--</option>
<option value="plane">Plane</option>
<option value="bike">Bike</option>
<option value="walk">Walk</option>
<option value="bus">Bus</option>
<option value="train">Train</option>
<option value="jetPack">Jet pack</option>
</select>
<select> 元素包含所有不同的值选择。在此处设置 id 属性以将控件与其标签关联,以及设置 name 属性以设置要提交的数据项的名称。
数据项的每个可能值都由一个 <option> 元素表示,嵌套在 <select> 元素内部。每个 <option> 元素都可以带一个 value 属性,该属性指定如果从下拉列表中选择该选项,则要提交的值。如果您不指定 value,则使用 <option></option> 标签内的文本作为值。
注意:如果您希望在页面加载时选择某个特定选项,可以将 selected 属性添加到相关的 <option> 元素。
多行文本输入字段
多行文本输入字段使用 <textarea> 元素创建
<label for="comments">Any other comments:</label>
<textarea id="comments" name="comments" rows="5" cols="33"></textarea>
它们的行为方式与 <input type="text"> 元素相同,只是它们允许多行文本输入。rows 属性指定文本区域默认的高度行数,而 cols 属性指定文本区域默认的宽度列数。如果未指定,则使用的值为 cols="20" 和 rows="2"。
大多数浏览器都会在文本区域的一个角落渲染一个拖动手柄,可用于调整其大小。尝试使用它来调整我们演示中文本区域的大小。
表单验证
早些时候,我们查看了浏览器提供的一些基本客户端表单验证。required 属性用于指定在提交表单之前必须填写字段;它还会检查是否为特定值类型(如电子邮件地址、URL、数字等)输入了正确的值类型。验证很重要,原因主要有两个
- 确保数据以正确的格式提交,以免在您的应用程序中引起错误。
- 确保数据不会引起安全问题。坏人知道如何以特定格式提交数据,以便在不安全的应用程序上,它可以执行删除数据库或控制系统的命令。
表单验证是一个巨大的主题,超出了本文的范围,因此我们暂时将其留在这里。请记住,有两种类型的表单验证
- 客户端验证,发生在浏览器中,使用表单验证属性(如
required)和 JavaScript 组合实现。客户端验证对于在用户输入错误数据时立即提供提示很有用,但在阻止恶意数据通过方面效果不佳。关闭 JavaScript 或修改客户端代码以使验证不再有效太容易了。 - 服务器端验证,发生在服务器上,使用服务器正在使用的任何语言实现。格式错误的消息可能会意外或故意发送到服务器。传统观点是确保您的服务器不信任客户端发送的任何内容,以避免因格式错误的消息引起的错误或安全问题。服务器端验证非常适合阻止恶意消息,因为篡改服务器上运行的代码更困难。服务器端验证在向用户提供有关不正确数据的提示方面效果不佳,因为数据必须发送到服务器进行验证,然后结果必须发送回客户端,用户才能收到通知。
简而言之,不要在客户端验证或服务器端验证之间做出选择——您需要两者。您需要客户端验证来向用户提供有关其输入的反馈,并需要服务器端验证来确保消息的格式您的服务器可以安全处理。如果您想开始学习更多关于验证的知识,一个好的起点是客户端表单验证。
总结
目前就这些了。关于表单还有很多需要了解的,但现在,我们已经为您提供了足够的理解,以便在学习中继续前进。
接下来,我们将为您提供一些测试,您可以用来检查您对我们提供的 HTML 表单信息的理解和掌握程度。