Skip to content
This page has been auto-translated and may contain errors.View in English

表单和输入

docs.scrimba.com

几乎每个为你做某些事情的网站,比如搜索框、登录、结账、评论字段,都是建立在表单基础上的。表单是页面停止向访问者讲话并开始听取意见的方式:它收集某人输入、勾选或选择的内容,并将该数据交给系统处理。本章讲的是使这一切成为可能的 HTML。

表单元素

**表单**是一个容器,它将访问者填写的内容组合在一起。你将输入框和按钮包装在 <form> 元素中,表单的作用是将所有内容收集起来,在访问者完成时将其发送出去。

html
<form>
  <label>你的名字</label>
  <input>
  <button>发送</button>
</form>

把它想象成你在医生办公室填写的纸质表格。这张纸保留了所有的方框,你在每个方框中填写,最后把整张纸一次性交回。<form> 元素就是那张纸。

<form> 是收集其控件并将其作为一个整体提交的容器。两个属性决定提交时发生什么:action 是数据发送到的地址,method 是数据发送的方式。

html
<form action="/subscribe" method="post">
  <label for="email">邮箱地址</label>
  <input id="email" name="email" type="email">
  <button type="submit">订阅</button>
</form>

当访问者触发提交时,浏览器会收集表单内每个已命名的控件,打包这些值,并向 action 地址发送请求。你不需要自己做任何连接。<form> 元素为你免费提供这种行为,这就是为什么将控件分组在真正的表单中比在页面上散放松散的输入框更好的原因。

<form> 是一种原生的**提交**机制:内置的浏览器行为,它将表单的控件聚集到一个请求中并发送它,无需任何脚本。提交时,浏览器读取 action(请求发送到的 URL)和 methodgetpost,本章稍后会讨论),将控件序列化为请求,并导航。这个默认行为就是为什么即使在 JavaScript 被禁用或未加载的情况下,表单仍然有效的原因,这是你从其上构建的基准,而不是你要替换的东西。

html
<form action="/subscribe" method="post">
  <label for="email">邮箱地址</label>
  <input id="email" name="email" type="email">
  <button type="submit">订阅</button>
</form>

有两种行为会让人困惑。首先,当焦点在单行文本字段中时,表单在按 Enter 时提交,而不仅仅是点击按钮,所以键盘提交是内置的,值得测试。其次,默认提交会重新加载或导航页面;当你用 JavaScript 处理表单时,你调用 event.preventDefault() 来停止导航并接管。把原生提交当作底线:即使在任何脚本运行之前,它也应该做一些明智的事情。渐进增强,在工作的 HTML 之上分层脚本,就是从这里开始的原因。

Juno表单元素<form> 是保存你的输入框和按钮的容器,当有人提交时,它会将所有内容一起发送。想象一下桌子上的纸质表格:一张纸,很多方框,一次性交回。在表单中包装你的字段,你就可以获得收集和发送行为,而无需做任何复杂的事情。
Juno表单元素<form> 将其控件捆绑在一起并提交它们;action 说明发送到哪里,method 说明如何发送。你可以免费获得收集和发送,所以使用真正的表单而不是松散的输入框加上你自己的点击处理器。它为你节省代码并在任何脚本加载之前就有效。
Juno表单元素 原生提交将控件聚集起来并向 action 发送请求,无需任何脚本参与,这正是为什么当 JavaScript 关闭时表单仍然有效的原因。记住它在文本字段中按 Enter 时提交,不仅仅是点击时提交。当你确实用 JavaScript 接管时,event.preventDefault() 停止默认导航,下面的纯表单仍然是你的安全网。

输入框和标签

<input> 是访问者输入内容的框。但单独来看,输入框只是一个空白框,不知道它的用途是什么。这就是**标签**的作用:它是告诉访问者在框中放什么内容的文字。

html
<label for="city">城市</label>
<input id="city">

每个输入框都需要一个标签。你通过给输入框一个 id 并用 for 指向它来连接这两个元素,在两个地方使用相同的单词。一旦它们被连接,点击标签会将光标放在框中,这是一个小小的善举,使表单对每个人都更容易使用。

<input> 收集单个值;<label> 为其命名。你通过匹配标签的 for 属性和输入框的 id 来关联它们:

html
<label for="username">用户名</label>
<input id="username" name="username" type="text" required>

有几个属性值得出现在大多数输入框上。name 是值发送时使用的键(在命名部分讨论)。required 阻止提交,直到字段被填充。placeholder 在框内显示淡色提示文本,但placeholder 不是标签:一旦有人开始输入,它就会消失,所以它不能替代真正的标签。每个输入框仍然需要自己的 <label>

有两种方式将 <label> 与其控件关联,这种区别对可访问性很重要。第一种是显式关联:标签携带一个 for 属性,其值等于控件的 id。第二种是隐式关联:你将控件包装在标签元素内,不需要 forid

html
<!-- 显式:for 匹配 id -->
<label for="phone">电话</label>
<input id="phone" name="phone" type="tel">

<!-- 隐式:输入框在标签内 -->
<label>
  电话
  <input name="phone" type="tel">
</label>

两者都给控件一个**无障碍名称**,这是屏幕阅读器在字段获得焦点时宣读的文本,所以看不到布局的访问者仍然知道要输入什么。优先选择显式关联:它可以在 CSS 布局将标签移离输入框时保持有效,而且当样式强制两者分离时也能工作。没有关联标签的字段被宣读为一个没有目的的裸编辑框,这是网络上最常见的可访问性失败之一。可访问性章节对此进行了更广泛的讨论。

Juno输入框和标签<input> 是人们输入内容的框,<label> 是告诉他们在那里放什么的文本。用匹配的 forid 连接它们,点击标签会直接跳到框中。给每个输入框一个标签;一个孤独的、旁边没有文字的框只会让人困惑。
Juno输入框和标签 将标签的 for 与输入框的 id 匹配,两者就被连接了。依靠 required 来阻止空提交,但不要让 placeholder 代替标签:一旦有人开始输入,它就会消失。真正的标签,每个字段,无一例外。
Juno输入框和标签 连接标签的两种方式:for 匹配 id,或在标签内包装输入框。两者都为字段提供一个无障碍名称,即屏幕阅读器读出的文本,而显式的 for/id 是当 CSS 将事物拉开时更坚固的选择。没有标签的字段读作一个没有名字的框,这是现实中最常见的可访问性遗漏之一。

其他控件

不是所有内容都是纯文本框。某些问题最好通过勾选、选择或从列表中选择来回答。勾选框(复选框)是一个开/关开关,下拉菜单让某人从几个选项中选择一个。

html
<label>
  <input type="checkbox"> 给我发送通讯
</label>

<label for="size">尺寸</label>
<select id="size">
  <option>小</option>
  <option>中</option>
  <option>大</option>
</select>

你不必记住这些。目前的要点是,表单可以用任何适合的方式提出问题:输入它、勾选它,或从列表中选择它。

<input> 元素根据其 type 属性改变形状,正确的类型为你提供更好的键盘、内置验证和本地选择器:

html
<input type="email">     <!-- 验证 @ 形状,移动设备上的邮箱键盘 -->
<input type="number">    <!-- 数字键盘,上下步进器 -->
<input type="password">  <!-- 掩盖字符 -->
<input type="date">      <!-- 本地日期选择器 -->
<input type="checkbox">  <!-- 单个开/关切换 -->
<input type="radio">     <!-- 从一组中选择一个 -->

除了 <input>,三个元素覆盖其余的。<textarea> 是用于较长写作的多行文本框。<select><option> 子元素是一个下拉列表。当你需要相关字段在视觉和语义上分组时,将它们包装在带有 <legend><fieldset> 中,该 <legend> 为组命名:

html
<fieldset>
  <legend>配送速度</legend>
  <label><input type="radio" name="speed" value="standard"> 标准</label>
  <label><input type="radio" name="speed" value="express"> 快速</label>
</fieldset>

<label for="message">消息</label>
<textarea id="message" name="message" rows="4"></textarea>

按钮也取 typetype="submit" 发送表单(表单内按钮的默认值),type="reset" 将每个字段清除回其起始值,type="button" 单独不做任何事情,是为 JavaScript 挂钩的。

每种控件类型都有值得在使用前了解的行为。单选按钮通过共享的 name 分组:一组中只有一个单选按钮可以被选中,而这种分组就是使它们互斥的原因,所以缺失或不匹配的 name 会悄悄地将组分解为独立的切换。

html
<fieldset>
  <legend>配送速度</legend>
  <label><input type="radio" name="speed" value="standard" checked> 标准</label>
  <label><input type="radio" name="speed" value="express"> 快速</label>
</fieldset>

<fieldset><legend> 不仅是视觉的。<legend> 由屏幕阅读器宣读为组内每个控件的上下文,所以"配送速度"字段集内的单选按钮读作"配送速度,标准",这是可理解的表单和一列无根据选项之间的区别。再有两个生产说明。复选框仅在勾选时发送其值;未勾选的框完全不出现在提交中,所以服务器看不到任何东西而不是 false,这决定了你如何在另一端读取数据。而且 type="submit" 是表单内 <button> 的默认类型,所以你打算用作 JavaScript 触发器的裸 <button> 会提交表单并重新加载页面,除非你设置 type="button"。这个默认值不断地让人们犯错。

Juno其他控件 表单可以用不止一种方式提问:是/否的勾选框,从列表中选择一个的下拉菜单。复选框是 <input type="checkbox">,下拉菜单是持有 <option> 选择的 <select>。不需要记住它们;当问题需要时,表单不仅仅有纯框。
Juno其他控件 输入框的 type 重塑它:emailnumberdatepasswordcheckboxradio,每种都有合适的键盘或选择器。针对长文本使用 <textarea>,针对下拉菜单使用 <select>,并用带有 <legend><fieldset> 对相关字段进行分组。注意按钮类型:submit 是表单内的默认值。
Juno其他控件 单选按钮通过共享的 name 分组,而字段集中的 <legend> 为每个控件提供说话上下文,所以它是结构,而不是装饰。两个陷阱:未勾选的复选框完全不发送任何东西,而不是 false,而裸 <button> 默认为 type="submit",所以除非你设置 type="button",否则它会重新加载页面。

表单数据如何命名和提交

当表单被发送时,每个答案都需要一个标签,这样接收者就知道它来自哪个框。该标签是输入框的 name。你设置一次,它就和值一起旅行。

html
<label for="city">城市</label>
<input id="city" name="city">

如果访问者在该框中输入"北京",表单会发送对"城市是北京"。name 是接收方区分一个答案和另一个答案的方式。没有 name 的输入框完全不出现在提交中,所以这个小属性是使数据实际出现的属性。

表单数据以一组**名称/值对**发送:每个控件的 name 成为键,访问者输入的内容成为值。只有带有 name 的控件被包括,所以 name 是将控件转变为已提交数据的东西。

html
<form action="/search" method="get">
  <label for="q">搜索</label>
  <input id="q" name="q" type="search">
  <label for="sort">排序方式</label>
  <select id="sort" name="sort">
    <option value="recent">最近</option>
    <option value="top">最高评分</option>
  </select>
  <button type="submit">搜索</button>
</form>

method 属性选择这些对如何传送。method="get" 将它们附加到 URL 作为查询字符串(/search?q=boots&sort=recent),这适合你可能书签或分享的搜索和筛选。method="post" 将它们放在请求体中看不见的地方,这适合任何改变数据或不应该在 URL 中的东西,比如密码或支付。

每次提交都是一组名称/值对,三个细节决定了它们如何编码和发送。

第一个是 methodget 将这些对序列化为 URL 中的查询字符串,即地址后面的 ?key=value&key=value 文本。这使请求可重复和可书签,这意味着值在 URL、浏览器历史和服务器日志中可见,所以 get 是为了读取(搜索、筛选),永远不是为了机密。post 在请求体中携带这些对,不在 URL 中,这是任何改变状态或敏感的选择。

第二个是 enctype编码,说明体的格式。默认值 application/x-www-form-urlencoded 将这些对打包为一个 key=value&... 字符串,这对文本来说很好。要发送选中的文件,你必须切换到 enctype="multipart/form-data",这是可以在文本字段旁边携带文件字节的格式;没有它,文件输入将无法正确上传。

html
<form action="/upload" method="post" enctype="multipart/form-data">
  <label for="avatar">头像</label>
  <input id="avatar" name="avatar" type="file">
  <button type="submit">上传</button>
</form>

第三个是 autocomplete,这是命名在 UX 和可访问性中获得回报的地方。autocomplete 令牌告诉浏览器字段在标准术语中的含义,所以它可以提供正确的已保存值:autocomplete="email"autocomplete="name"autocomplete="current-password"autocomplete="street-address"。这些令牌是固定词汇表,而不是自由文本,获取它们正确让浏览器或密码管理器一点击就填充表单,这对在手机上输入或使用辅助技术的人来说最重要。

html
<label for="email">邮箱</label>
<input id="email" name="email" type="email" autocomplete="email">

保持内置检查诚实是一个单独的工作,在表单验证章节中讨论;本节关于的是数据有效后如何塑形和发送。

Juno表单数据如何命名和提交 每个输入框的 name 是其答案上的标签,所以接收者知道一个值来自城市框而不是邮箱框。设置 name,框的值作为标记对被发送。忘记它,该字段被无声地留出,这在第一次表单不发送任何东西时会绊倒很多人。
Juno表单数据如何命名和提交 已提交的数据是名称/值对,只有带有 name 的控件被包括。选择 method 来匹配工作:get 将值放在你可能书签的 URL 中进行搜索和筛选,post 将它们隐藏在请求体中,用于任何改变数据或保存机密的内容。密码在 URL 中是永远不要犯的错误。
Juno表单数据如何命名和提交get 将这些对写入 URL,所以它是为了读取,永远不是为了机密;post 在体中携带它们。切换 enctypemultipart/form-data 或文件输入将无法上传。并花费精力在 autocomplete 令牌上,如 emailcurrent-password:它们是固定词汇表,获取它们正确让手机或密码管理器一点击填充表单。