学习和获得帮助 - 学习 Web 开发

学习和获得帮助
=======

你花一些时间学习一套新技能是很好的，但采用一些更好的做法可以让你的学习更加高效。有时，你也会陷入困境并感到沮丧，即使是专业的 Web 开发人员也会经常这样，因此，了解最有效的方法，尝试获得帮助，以便你能够继续工作，是值得付出的。本文在这两个方面提供了一些提示和提示，可帮助你从学习 Web 开发中获得更多内容，并进一步阅读，以便你能够找到有关每个子主题的详细信息。

有效学习
----

让我们直接思考一下有效的学习。

### 不同的学习方法

有趣的是，你的大脑学习东西有两种主要方式——**专注**和**分散学习**：

* 重点学习是你传统上可能与学术科目联系在一起的东西。你专注于一个低级的话题，并解决它带来的具体问题。你专注于一个狭窄的区域。
* 分散学习更多的是与围绕更广领域的高层次思维有关。你让你的头脑游荡更广，似乎在不同事物之间随机联系。这更多的是你在洗澡时或喝咖啡休息时的想法。

从神经科学家对大脑活动所做的研究中，我们发现，你不能同时参与两种学习或思考的方式。那么，你应该选择哪一个呢？你可能认为集中学习更适合学习，但在现实中，**两者**都很重要。

专注思维对于将精力集中在特定主题上，深入解决问题，以及提高你对所需技术的掌握——加强大脑中存储信息的神经通路——都很棒。然而，当你试图理解新学科或解决以前没有遇到的新问题时，它并不善于理解"大局"，并解锁新的神经通路。

为此，你需要漫不由多的思考。这与焦点正好相反——你让你的大脑在更广阔的环境中游荡，四处寻找你之前没有的联系，接触新事物（或新事物的新组合），然后你可以关注这些事物，加强它们，并开始真正理解它们的意思。

这就是为什么在进入具体细节之前，先阅读一些介绍性材料，以便对一个领域有一个高度的了解，这通常是好的。

这也是为什么你有时真的可以陷入一个问题，但随后找出答案，当你去喝咖啡休息（或散步）。你可以：

1. 知道如何使用工具 A 解决问题 A。
2. 知道如何用工具 B 解决问题 B。
3. 不知道如何解决问题 C。

假设你一直专注于问题 C，然后感到沮丧，因为你无法思考如何解决它。但是，在散步获得新鲜空气后，你可能会发现，当你的头脑徘徊，你突然使工具 A 和工具 B 之间的连接，并意识到你可以使用它们一起解决问题 C！它并不总是这么简单，但它也令人惊讶的是多少次，这确实发生了。这也凸显了在电脑前学习时定期休息的重要性。

### 不同的学习材料

也值得看看可用的不同类型的学习材料，看看哪些材料对你来说最有效。

#### 文本文章

你会发现很多书面文章在网上教你有关网页设计。例如，像本课程的多数课程一样。有些文章将是教程，教你某种技术或重要概念（如"学习如何创建视频播放器"或"学习 CSS 框模型"），有些文章将是参考材料，允许你查找你可能忘记的细节（如"CSS 属性的语法是什么"？`background`

MDN Web 文档对这两种类型都非常好 - 你当前位于的区域非常适合学习技术和概念，我们还有几个巨大的参考部分，允许你查找任何你不记得的语法。

网上还有其他几个伟大的资源，我们将在下面提及其中一些资源。

**备注：** 上面的文本应该给你一个重要的事实 - 你不应该记住一切！专业的 Web 开发人员使用 MDN Web 文档等工具查找他们一直忘记的内容。正如你会发现，学习 Web 开发更多的是关于问题解决和学习模式，而不是学习大量语法。

#### 视频资料

还有一些网站上有视频学习内容。很明显 YouTube 是一个不错的网站，它有很多频道，比如 Mozilla Layout Land、 MozillaDeveloper 和 Google ChromeDevelopers 提供许多有用的视频。许多人更喜欢文本文章来进行更深入的学习和参考资料，喜欢视频来快速理解概念和新功能，但你更喜欢从中学习什么实际上取决于你自己。这里没有对错的说法。

#### 交互式的代码演练场

你可能是那种喜欢简单指令的人，更喜欢直接开始操纵代码。这也是一种合理的方法，一些学习网站倾向于采用这种方法。例如像 Codecademy 这类网站中，教程主要由交互式代码编辑器组成，你必须直接编写代码并查看是否达到了预期的结果。

许多 MDN Web 文档参考页面中也提供了交互式的示例，你可以在其中更改代码，并查看实时结果的变化。在你的计算机上或者在像 JSBin、Codepen 或 Glitch 这样的在线代码编辑器中创建自己的代码示例也是可以的。事实上，当你在学习时，你会被要求去做这些事情本身就是属于课堂的一部分！

**备注：** 在线代码编辑器对于共享你编写的代码也非常有用，例如，如果你正在与不在同一位置的其他人协作学习，或者正在向他人发送代码以寻求帮助。你可以与他们共享代码示例的网址，以便他们可以查看。

**备注：** 与其他学习方法相比，你可能更喜欢一种学习方法，但实际上，你最终可能得到的是一种混合方法。你可能还会想出另外其他的方法，而不是我们上面提到的三种。

### 制定一个计划

制定一个计划来帮助你通过学习实现你想要实现的目标是个好主意。

#### 定一个目标

这听起来很傻，但为什么不从一句话开始，说出你想要实现的目标呢？以下内容有不同的见识，但都是现实可行的：

* 我想在两年后成为一名专业的 Web 开发者。
* 我想学习足够的知识，为我当地的业余网球俱乐部建立一个网站。
* 我想学习 HTML 和 CSS，这样我就可以扩展我的工作角色，接管更新我们公司网站上的内容。

下面是一些不切实际的想法：

* 我想在三个月内从一个完全的初学者变成一名高级 Web 开发人员。
* 我想创办自己的公司，建立一个社交网络，在两年内超越 Facebook。

#### 你需要什么才能到达那里？

一旦你制定了目标，研究一下实现目标需要什么是个好想法。例如：

我需要的工具：

* 一台计算机
* 接入互联网
* 一些笔和纸

我需要的知识：

* 如何使用 HTML、CSS、JavaScript 以及相关的工具和最佳实践来构建网站和 web 应用程序（我们绝对可以在这方面为你提供帮助！）。
* 如何得到域名和主机，然后使用它们将网站发布到互联网上。
* 如何经营一家小企业。
* 如何为我的生意做广告，吸引客户。

#### 这需要花费多少时间和金钱？

预估得到这些东西的时间和成本。如果你需要工作来挣钱购买所需的材料，那么时间成本就必须考虑在内。一旦你有了一个时间估计，你就可以开始围绕你的生活制定一个计划。

#### 我每周需要投入多少小时？

一旦你知道你需要做什么，以及你认为需要多长时间，你就可以开始写一个计划来实现你的目标。它可以简单到：

“我需要花 500 个小时来学习我需要知道的东西，我有一年的时间来学习。如果我假设有两周的假期，我打算每周为此学习 10 个小时 (在晚上和周末有空的时候)，所以我会这样安排时间。”

当然，你能在这上面花多少时间取决于你的情况。如果你在学校，那么你有比其他有工作和孩子要养活的人地情况更多的空闲时间。如果不是，实现目标仍然是可能的，但你必须现实清楚地知道实现目标最快需要多长。

如果你正在大学或学院学习网络开发课程，那么大部分计划都是为你做的——你是幸运的！

当你制定了一个每周的日程安排之后，你应该在一个简单的电子表格中，甚至在笔记本上记录下你每周要做的事情！

此外，制定一些次级目标可能是个好主意，这样你就可以更容易地了解自己处在什么阶段。例如：

* 暑期学习 HTML 和 CSS 基础知识
* 在 12 月学习完 JavaScript 基础知识
* 在明年的 4 月份前搭建好一个示例网站
* 等等。

经常想一想你取得了多大的进步，在需要的时候及时调整你的计划。

### 保持动力

很难保持动力，尤其是当你试图学习编程或网络开发等复杂技能时。以下是一些保持动力并持续工作的建议：

* **尽量让你的工作环境更有效率**。找一张舒适的办公桌椅，确保你有足够的光线看清楚你在做什么，并尽量提供有助于你集中注意力的东西（如柔和的音乐、香水，任何你需要的东西）。不要试图在一个分心的房间里工作——比如开着电视，朋友们在看足球！另外，把手机放在房间外——大多数人都会被手机分心，所以你应该把它放在其他地方。
* **定期休息**。不间断地工作几个小时对你的动力不好，尤其是当你发现工作很难或遇到问题时。这只会导致沮丧。通常最好休息一下，四处走动一会儿，然后在回去工作之前喝一杯咖啡或者饮料放松。正如我们之前所说，你在这段时间里所做的分散式学习通常可以帮助你找到解决你所面临问题的方法。长时间不休息也会对身体有害；盯着显示器看太久会伤害你的眼睛，久坐不动会对你的背部或腿部有害。我们建议每小时到 90 分钟这个区间内后休息 15 分钟。
* **吃饭、锻炼和睡觉**。健康饮食，经常锻炼，确保充足的睡眠。这听起来很明显，但当你真正开始编码时，很容易忘记。将这些基本要素纳入你的时间表，确保你除此之外没有安排更多的学习时间。
* **奖励你自己**。可以肯定的一点：*拼命工作而不娱乐会使你沉闷*。你应该尝试在每一次的学习之后安排一些有趣的事情，前提是你必须在完成学习任务之后。例如，如果你真的喜欢玩游戏，那么说“除非我完成了 5 个小时的学习，否则今晚不要玩游戏”是很有激励作用的。现在你只需要意志力。希望一些顺利。
* **共同学习和演示**。这不是每个人的选择，但如果可能的话，试着和其他人一起学习。再说一次，如果你正在上一门关于网络开发的大学课程，这会更容易，但也许你可以说服一个朋友和你一起学习，或者找一个当地的聚会或技能分享小组？有人与你讨论想法并寻求帮助真的很有用，也很激励人，你还应该花时间向你的朋友展示你做了什么小样作品。那些感激的喊声会激励你前进。
### 高效解决问题

没有一种有效的方法可以解决与 web 设计和开发相关的所有问题（学习所有东西），但在大多数情况下，有一些通用的建议可以很好地为你服务。

#### 把东西分解成各个小块

首先，当你试图实现某个特定的东西时，你的头脑似乎真的很难理解，你应该试着把它分解成多个较小的问题或块。

例如，如果你正在考虑“构建一个简单的两栏网站”的任务，你可以将其分解如下：

* 组织 HTML 结构
* 制定基本的网站排版
* 确定基本的网站颜色主题
* 实现高级布局——页眉、水平导航菜单、带有主栏和侧栏的主要内容区域以及页脚
* 实现一个水平的导航菜单
* 等等。

然后你可以进一步分解它。例如，“实现水平导航菜单”可以写成：

* 列出水平排列的菜单项。
* 删除不必要的默认设置，如列表间距和项目符号。
* 适当设置菜单项的悬停/焦点/激活状态。
* 使菜单项沿直线等距排列。
* 给菜单项留出足够的垂直间距。
* 确保文本在每个菜单项内居中。
* 等等。

这些问题中的每一个似乎都不像你开始遇到的一个大问题那么难解决。现在你只需要逐步想出方案并解决所有问题！

#### 学习并认知模式

正如我们之前所说，web 设计/编程主要是关于问题解决和模式。一旦你写下了你需要做什么来解决一个特定的问题，你就可以开始弄清楚用什么技术特性来解决它了。例如，专业的 web 开发人员已经创建了很多水平导航菜单，所以他们会立即开始考虑这样的解决方案：

创建一个水平导航栏菜单经常利用到列表，比如：

```html
<ul>
  <li>First menu item</li>
  <li>Second menu item</li>
  <li>Third menu item</li>
  <li>etc.</li>
</ul>
```

要使所有内容水平放置在一条线上，最简单的现代方法是使用 flexbox：

```css
ul {
  display: flex;
}
```

接着删除不必要的间距和要点，我们可以这样做：

```css
ul {
  list-style-type: none;
  padding: 0;
}
```

等等。

如果你是一个完全的网络开发初学者，你将不得不做一些研究和网络搜索和查找解决方案来解决这些问题。如果你是一名专业的 web 开发人员，你可能还记得上次解决类似问题的时候，只需要查找一些你后面遗忘的语法。

当你找到这些问题的解决方案时，写下你所做的事情，并在某个文件目录中保留一些少量的代码示例，这样你就可以回顾以前的工作了。

另外，网络上有 开发者工具，它使你可以查看用在 web 上构建任何网站的代码。如果你手头没有解决方案，一个好的研究方法是在网络某处地方找到具有类似功能的网站，并找出它们是如何做到的。

**备注：** 请注意，上面我们讨论了我们首先要解决的问题，然后是用来解决这个问题的技术。这几乎总是最好的方法--不要从一个你看起来很酷但陌生的新技术开始，然后试着把它塞进用例中。

**备注：** 最简单的解决方案往往是最好的。

### 做练习

你练习解决问题越多，你的大脑在该区域的神经通路就越强大，也就越容易回忆起特定问题的细节和逻辑。

继续修补代码，并进行更多练习。如果你没有问题要解决，可以在网上查一些测试，多上一些课程，或者问问你的朋友和家人（或当地的学校或教堂），他们是否希望你为他们做些什么。

寻求帮助
----

网络开发需要你学习一套复杂的技能——有时你肯定会陷入困境，需要帮助。正如我们之前所说，即使是专业开发人员在某些时刻也需要让他人来帮助解决问题。

获得帮助的方法有很多种，下面是一些非常有效的方法。

### 高效的网络搜索

需要学习的一项重要技能是高效网络搜索的技巧——你需要在你最喜欢的搜索引擎中使用哪些搜索词来找到你需要的文章？

搜索什么通常是相当直接的。例如：

* 如果你想了解更多关于响应式网页设计的信息，你可以搜索”响应式网页设计”。
* 如果你想了解更多关于某项特定技术功能的信息，像 HTML 元素，CSS 属性，或者 JavaScript 方法，你应该直接搜索功能的名称。`<video>` `background-color` `opacity` `Date.setTime()`
* 如果你正在寻找一些更具体的信息，你可以添加其他关键字作为补充，例如 "<video> element autoplay attribute"，或者 "Date.setTime parameters"。

如果你想搜索一些没有那么明显的专业术语的东西，你需要考虑最有可能搜索到想要的东西是什么。

* 在好几个 promises 执行之后运行代码
* 在浏览器中播放网络摄像头中的视频流
* 在元素的背景中创建线性渐变

#### 报错信息

如果你的某段代码有问题，并且出现了一条特定的错误消息，那么把错误消息将其用作搜索词复制到你的搜索引擎中。如果在之前其他人也有同样的问题，在 MDN 或 Stack Overflow 之类的地方可能会有一些关于它的文章或博客帖子。

**备注：** Stack Overflow 是一个非常有用的网站——它基本上是一个关于各种技术和相关技术的精心挑选有质量的问题和答案的巨大数据库。你可能会找到一个回答你问题的答案。如果没有，你可以问一个问题，看看是否有人能帮助你。

#### 浏览器调试

看看你的问题是否在所有浏览器中都出现，或者它是否只发生在一个或少数浏览器中，这通常是一个好主意。例如，如果它只影响一个浏览器，则可以使用该浏览器缩小搜索范围。示例搜索可能看起来像：

* <video> 在 iOS 浏览器中播放不起作用。
* Firefox 浏览器似乎不支持 Beetlejuice API。
### 使用 MDN

你现在所在的网站上有大量信息可供你使用，既有查找代码语法的参考资料，也有学习技巧的指南/教程。

在 MDN 的这一部分中，我们提供了关于 web 开发基础知识的大部分问题的答案。如果你陷入困境，最好重新阅读相关文章，看看你是否遗漏了什么。

如果你不确定该读哪篇文章，那么尝试在 MDN 中搜索一些相关的关键字（如上所述），或者尝试一般的网络搜索。要在 MDN 上搜索，你可以使用网站内置的搜索功能，或者使用你最喜欢的搜索引擎，在搜索词前面加上“MDN”。例如，“mdn responsive web design“或者”mdn background-color“。

### 其他在线资源

我们在之前已经提到了 Stack Overflow，但在这里还有其他在线资源可以提供帮助。

找到一个社区是件好事，如果你试图帮助别人回答他们的问题，也可以自己在上面提出问题，你会得到很多尊重。其他好论坛包括：

* MDN Discourse
* Sitepoint Forums
* webdeveloper.com Forums

然而，在 Twitter 或 Facebook 等社交网站上寻找有用的群组也是有意义的。寻找讨论你感兴趣的网络开发主题的小组，并加入其中。在 twitter 上关注那些你知道有影响力、聪明或平易近人的人，他们似乎分享了很多有用的建议。

### 线下聚会

最后，你应该尝试参加一些线下聚会，认识其他志同道合的人，尤其是那些对初学者友好的人。meetup.com 是一个很好的地方，可以找到当地的身体聚会，你也可以尝试搜索你当地的媒体/网站上的内容。

你也可以尝试参加正式的网络会议。虽然这些费用可能很高，但你可以尝试在那里做志愿者，许多会议都提供优惠票，例如学生票或多样性票。

参见
--

* Coursera: Learning to learn
* Freecodecamp
* Codecademy


Web 表单指南 - 学习 Web 开发

Web 表单指南
========

这个模块提供了一系列帮助你掌握 web 表单的文章。web 表单是与用户交互的强大工具，它们经常用于收集用户的数据，或允许他们控制用户界面。然而，由于历史和技术上的原因，如何充分发挥它们的潜力并不总是显而易见的。在下方列出的文章中，我们将介绍 Web 表单的各个方面：HTML 结构、样式、验证表单数据，以及提交数据到服务器。

#### 想成为一名前端网页开发者？

我们整理了一个课程，其中包含你实现目标所需的所有基本内容。

**开始使用**

前提
--

在开始这个模块之前，你至少应该熟悉对 HTML 的介绍系列。此时此刻，你应该会发现基本指南很容易理解，并且能够使用我们的原生表单控件指南。

然而，掌握表单需要的不仅仅是 HTML 知识——你还需要学习一些特定的技术来设置表单控件的样式，并且需要一些编码知识来处理诸如验证和创建自定义表单控件之类的事情。因此，在你查看下面列出的其他部分之前，我们建议你先离开，并学习一些 CSS 和 JavaScript。

以上内容明确地说明了为什么我们将 web 表单独立放在一个模块中，而不是将其与 HTML、CSS 和 JavaScript 这些模块放在一起——表单元素比绝大多数其他的 HTML 元素都要复杂，并且它们还需要与相关的 CSS 和 JavaScript 技术紧密集合才能充分利用它们。

**备注：** 如果你正在使用一个无法创建自己的文件的计算机、平板电脑或其他设备，那么你可以尝试（大多数）在线编码程序中的代码示例，例如 JSBin 或 Glitch。

基本指南
----

你的第一个表单

本系列的第一篇文章提供了第一次创建 HTML 表单的经验，包括设计一个简单表单、使用正确的 HTML 元素实现它、通过 CSS 添加一些非常简单的样式，以及如何将数据发送到服务器。

如何构造 HTML 表单

有了基础知识，我们现在更详细地了解了用于为表单的不同部分提供结构和意义的元素。

不同的表单控件
-------

原生表单控件

我们从详细了解原始 HTML `<input>` 元素的类型开始，同时学习在收集不同类型数据时可用的选择。

HTML5 input 的类型

在这里，我们深入挖掘 `<input>` 元素，了解 HTML5 发布时提供的其他 input 类型，以及它们提供的各种 UI 弓箭和数据收集增强功能。此外，我们可以了解 `<output>` 元素。

其他表单控件

接下来，我们学习所有非 `<input>` 表单控件，以及相关的工具，如：`<select>`、`<textarea>`、`<meter>` 和 `<progress>`。

表单样式指南
------

HTML 表单样式

本文介绍了如何使用 CSS 为表单赋予样式，包括可能需要了解的基本样式任务的所有基础知识。

高级表单样式

在这里，我们将看到一些更高级的表单样式技术，这些技术需要在处理一些更难以设置样式的元素时使用。

UI 伪类

这是一篇对 UI 伪类的介绍，使 HTML 表单控件能够根据当前状态进行定位。

验证和提交表单数据
---------

客户端表单验证

仅仅发送数据还不够——我们还需要确保用户填写表单的数据格式是正确的，以在后续流程中正确处理它，而且它不会破坏我们的应用程序。我们还希望帮助用户正确填写表单，让他们在使用应用程序时不会感到困扰。表单验证可以帮助我们实现这些目标，本文将告诉你需要了解的内容。

发送表单数据

本文讨论当用户提交一个表单时，会发生什么情况——表单数据的去向，以及当表单数据到达指定位置时我们如何处理？我们还研究了与发送表单数据相关的一些安全问题。

高级指南
----

以下文章对于学习路径来说没那么重要，但是当你掌握了上述技术并向了解更多内容时，它们会变得有趣且实用。

如何构建自定义表单控件

在某些情况下，可能出于样式或功能的原因，原生表单部件无法提供你所需要的东西。在这种情况下，你可能需要使用原生 HTML 构建自己的表单小部件。本文将讲述一个实例，说明你将如何做到这一点，以及在实际操作时需要注意的事项。

通过 JavaScript 发送表单

本文将讨论如何使用表单来构建 HTTP 请求，并通过定制的 JavaScript 发送它，而不是标准的表单提交。它还说明了为什么要这么做，以及这样做的意义。（请参阅使用 FormData 对象。）

表单控件的 CSS 属性兼容性表

最后一篇文章提供了一个方便的参考，以确定 CSS 属性与表单元素的兼容性关系。

参见
--

* HTML 表单元素
* HTML <input> 元素
* HTML 属性


无障碍 - 学习 Web 开发

无障碍
===

如果你想成为一个 web 开发者，学习一些 HTML、CSS 和 JavaScript 是很有用的，但是仅仅使用这些技术是不够的，重要的是要学会如何**负责任**地使用这些技术，以便所有读者都可能在网上使用你的创作。为了达到这个目的，要遵循一般最佳实践（这些最佳实践已经被 HTML、CSS 和 JavaScript 相关的主题所证明）进行跨浏览器测试，并且从项目最初阶段就把无障碍性考虑在内。在这个模块中我们将会详细讨论后者。

概述
--

当有人将一个网站描述为“无障碍”时，他们的意思是，任何用户都可以使用其所有的功能和内容，无论用户是如何访问网络的——甚至特别是有身体或精神障碍的用户。

* 键盘、鼠标和触摸屏用户，以及用户访问网络的任何其他方式，包括像 Alexa 和 Google Home 这样的屏幕阅读器等语音助手，都应该能够访问网站。
* 无论人们的听觉、视觉、身体或认知能力如何，应用程序都应该是可以理解和使用的。
* 网站也不应该造成伤害：像动画（motion）这样的 web 特性会导致偏头痛或癫痫发作。

**默认情况下，HTML 在使用正确的时候是可以实现无障碍的**。Web 无障碍涉及确保内容保持无障碍，无论访问 web 的人员或方式。

Firefox 浏览器无障碍检查器是一个非常有用的工具，用于检查网页的无障碍问题。下面的视频对它做了很好的介绍：

#### 想要成为一名前端工程师？

我们汇总了一门课程，其中包含你实现成为前端工程师目标所需要的所有基本信息。

**从这里开始**

前提
--

为了充分理解这个模块，你至少需要读过 HTML、CSS 和 JavaScript 各个主题的前两个模块，或者有可能的话，在你学习相关联的技术话题时也把无障碍相关的内容学习了，这样会更好。

**备注：** 如果在你所使用的设备上不能建立自己的文件，你可以使用在线编码平台运行大多数样例代码，比如 JSBin 或者 Glitch。

指南
--

什么是无障碍？

这篇文章开头很好地阐述了到底什么是无障碍——这包括我们要考虑哪一群人，为什么要考虑这些人，不同用户使用什么工具与网页交互，以及我们如何在 web 开发工作流中构建无障碍部分。

HTML: 无障碍的良好基础

只要确保恰当的 HTML 元素被用于适当的目的，大量的网页内容即可具有无障碍，这篇文章详细地讲述了 HTML 的用法，以确保网页有最大的无障碍性。

CSS 与 JavaScript 无障碍最佳实践

当正确使用 CSS 与 JavaScript 的时候，也能够使得网页具有很好的无障碍，但是如果错误地使用它们将极大地破坏无障碍。这篇文章罗列了一些应该被考虑的 CSS 和 JavaScript 的最佳实践，确保再复杂的内容都可以有尽可能好的无障碍。

WAI-ARIA 基础

继上一篇文章之后，有时候制作复杂的 UI 控件会涉及到非语义的 HTML 和动态的 JavaScript 更新内容会很难。WAI-ARIA 是一种可以帮助解决这些问题的技术，通过增加浏览器和辅助技术可以识别和使用的进一步语义来让用户知道正在发生的事情。在这里，我们将展示如何在基本级别上使用它来提高无障碍。

多媒体无障碍

可以提高无障碍的另一类内容是多媒体——视频，音频和图像内容需要提供适当的文本替代方案，以便辅助技术和相应的用户能够理解。这篇文章展示了如何这样做。

移动端无障碍

随着移动设备上网页访问的流行，以及主流平台（如 iOS 和 Android）具备了完善的辅助功能工具，考虑在这些平台上 Web 内容的无障碍也是非常重要的。本文讨论针对移动设备的无障碍注意事项。

评估
--

无障碍诊断

在这个模块的评估部分中，我们向你展示了一个简单的网站，它有几个无障碍问题，需要你进行诊断和修复。

参见
--

* 现在开始构建无障碍的 web 应用——来自 Marcy Sutton 的一系列优质的视频教程。
* Deque University 资源——包含代码示例，屏幕阅读参考和其他有用的资源。
* WebAIM 资源——包含指引、清单、工具和其他资源。
* Web 无障碍性评估工具列表——包含一系列 web 无障碍评估工具。


使用 MathML 编写数学公式 - 学习 Web 开发

使用 MathML 编写数学公式
================

数学标记语言（Mathematical Markup Language）或 MathML 是用于在网页中编写数学公式的标记语言，其支持分数、上下标、根号、矩阵、积分、级数等。尽管它最初被设计为独立的 XML 语言，但 MathML 通常嵌入在 HTML 文档中，并可视为 HTML 的扩展。

**警告：** 实际上，MathML 内容是由轻量级标记语言（例如 LaTeX）或图形用户界面生成的。如果你只需要在网页中集成数学公式，可以参考编写 MathML 页面上的提示。

#### 想成为前端开发人员吗？

我们提供了一门课程，包含了你实现这个目标所需的所有基本信息。

**开始学习**

前提
--

在尝试学习 MathML 之前，我们假设你已经具备一些基本的 HTML 和 CSS 知识。因此，强烈建议你首先熟悉这两种技术。请从以下模块开始：

* Web 入门
* HTML 简介
* CSS 入门

尽管所需的概念将根据需要进行解释，但对数学符号和 TeX 渲染规则有一定的了解可能会对你有帮助。

模块
--

MathML 入门

MathML 是用于在网页中编写数学公式的标记语言。该模块将以比较轻松的方式开始你的 MathML 掌握之旅，介绍其工作原理、语法以及如何在 HTML 中开始使用它。

参见
--

MDN 上的 MathML

MathML 文档在 MDN 上的主要入口，你可以在此找到 MathML 语言所有特性的详细参考文档。想知道某个属性可以使用哪些值？这是一个很好的参考资料。


服务器端网页编程 - 学习 Web 开发

服务器端网页编程
========

***动态网页——服务器端编程***这主题是一系列的模块来演示如何创建动态的网页；可以交付自定义的信息来回应 HTTP 请求的网页。这些模块为服务器端编程提供了一个通用的介绍，以及如何使用 Django (Python) 和 Express (Node.js/JavaScript) 去创建基础应用的具体的入门指导。

大多数的主流网页使用一类服务器端的技术去动态地显示所要求的不同数据。举个例子，想象一下 Amazon 上有多少可购买的产品，以及 FaceBook 上有多少帖子？用完全不同的静态页面去显示所有的这些内容会彻底地低效，所以取而代之的是这些网站展示的是静态的模板（用 HTML, CSS, 和 JavaScript 构建），然后在有需要时动态地在这些模板中更新数据展示，比如说当你想要在 Amazon 上浏览一个不同的产品。

在现代的网页开发世界里，学习服务器端开发是高度推荐的。

学习路径
----

开始服务器端编程通常比客户端编程要简单，因为动态的页面倾向于执行非常类似的操作（从数据库中获取数据然后显示到一个页面中，确认用户输入的数据以及保存到一个数据库中，检查用户的权限和登陆用户，以及更多），并且它是用能使这些和其他的常见网页服务端操作变简单的网页框架来构建的。

知道一些关于编程概念（或者关于一个特定的编程语言）的基础知识会很实用，但不是必要的。类似的，精通客户端编程也不是必修的，但一些基本知识会帮助你和创建你的客户端的“前端”开发者更融洽地工作。

你会需要去理解“网页是如何工作的”。我们推荐你先去阅读以下主题：

* 什么是一个网页服务器 [What is a web server]
* 我需要什么软件去构建一个网页？ [What software do I need to build a website?]
* 你怎样上传文件到一个网页服务器？ [How do you upload files to a web server?]

拥有这些基础理解，你会做好完成在这节中的模块的准备。

模块
--

这个主题包含了以下的模块。你应该从第一个模块开始，然后接着到后面的任一模块，后面的模块演示了如何使用两个应用了合适的网页框架的非常流行的服务器端语言。

服务器端编程的第一步 [Server-side website programming first steps]

这个模块提供了关于服务器端网页编程的服务器技术无关的信息 [server-technology-agnostic information]，包括了关于服务器端编程的根本问题的答案——”它是什么“，”它跟客户端编程的区别“，和”为什么它很实用“——以及关于一些流行的服务器端框架的概述和如何为你的网站选择最合适的框架的指南。最后我们提供了一个关于网页服务器安全的介绍性部分。

Django 网页框架 [Django Web Framework (Python)]

Django 是一个非常流行以及功能齐全的服务器端网页框架，它是用 Python 编写的。这个模块讲解了为什么 Django 是一个这么好的网页服务器框架，如何设立一个开发环境以及如何使用它来执行常见的任务。

Express 网页框架 [Express Web Framework (Node.js/JavaScript)]

Express 是用 JavaScript 编写并在 node.js 运行时环境中托管的一个流行的网页框架。这个模块讲解了这个框架的一些主要优点，如何设立你的开发环境以及如何执行常见的网页开发和部署的任务。


使用 HTML 组织网站内容 - 学习 Web 开发

使用 HTML 组织网站内容
==============

为了创建一个网站，你需要了解 HTML——一项用于定义网页结构的基本技术。HTML 用于标识你的网页内容是应该被解析为段落、列表、头部、链接、图像、多媒体播放器、表单或是其他众多可用的元素之一，亦或是你定义的新元素。

#### 想要成为一名前端工程师？

我们汇总了一门课程，其中包含你实现成为前端工程师目标所需要的所有基本信息。

**从这儿开始**

须知
--

在开始这个主题的学习之前，你至少要基本熟悉使用电脑和被动地使用网络（即单纯地查看内容）。你应该设置好一个基础的工作环境，详细参照安装基础软件，并且理解如何新建和管理文件，详细参照文件处理——这两者都在 web 入门的零基础模块里。

在尝试学习这个主题之前，建议先完成 web 入门主题，不过这并不是必要的。HTML 基础里大多数的文章在 HTML 介绍中也有涉及，不过要详细得多。

在学习了 HTML 之后，你就可以继续学习其他进阶主题了，例如：

* CSS，以及如何用它装饰 HTML (例如：更改你的文本字号和字体，添加边框和阴影，将你的页面设计成多栏布局，添加动画和其他视觉效果。)
* JavaScript，以及如何用它为网页添加动态功能（例如：找到并在地图上绘制出你的地址，触发按钮时让 UI 元素显示或消失，将用户的数据本地储存在他们的电脑里等等。）
模块
--

这个主题由包含以下模块，建议从第一个开始，按顺序进行学习。

HTML 介绍

这一模块将为你铺路，帮你习惯一些重要的概念和语法，着眼于如何对文本应用 HTML，创造超链接，以及使用 HTML 构造一个网页。

多媒体和嵌入

这个模块带你探索如何使用 HTML 在你的网页里如何包含多媒体信息，例如用各种方法包含图片，以及嵌入视频、音频，甚至是嵌入其他完整的网页。

HTML 表格

在网页上用易于理解和无障碍的方式来表示表格数据是一项挑战。这个模块包括了基本的表格标记及更多复杂的特性，例如实现标题和总结。

解决常见的 HTML 问题
-------------

使用 HTML 解决常见问题提供了一系列在创建网页过程中可能遇到的常见问题的解决方案：处理标题，添加图片或视频，强调内容，创建一个基础表单等等。

参见
--

web 表单

本模块提供了一系列文章以助你掌握 web 表单的要领。web 表单是一种非常强大的用户交互工具--其最常见的应用是收集用户的数据，或允许他们控制用户界面。然而，由于历史和技术原因，想要充分发挥他们的潜能并不是一件易事。本模块将介绍 web 表单的所有基本要素，包括标记其 HTML 结构、表单控件的样式、验证表单数据以及向服务器提交数据。

MDN 上的 HTML（超文本标记语言）

MDN 上 HTML 相关参考文档的主入口，涵盖了详细的元素和属性参考——比如说如果你想知道一个元素有什么属性或者一个属性有什么值，此页面是一个查询它们的好地方。


发送表单数据 - 学习 Web 开发

发送表单数据
======

* 上一页
* Overview: Forms
* 下一页  (en-US)

本文将讨论当用户提交表单时发生了什么——数据去了哪，以及当它到达时该如何处理？我们还研究了与发送表单数据相关的一些安全问题。

|  |  |
| --- | --- |
| 前提： | 基本计算机素养，对HTML 的理解，对HTTP 和服务器端编程的基础知识。 |
| 目标： | 了解表单数据提交时发生了什么，包括服务器上如何处理数据的基本概念。 |

数据都去哪儿了？
--------

在这里，我们将讨论在提交表单时数据会发生什么。

### 客户端/服务器体系结构

web 基于非常基本的客户端/服务器体系结构，可以总结如下：客户端（通常是 web 浏览器）向服务器发送请求 (大多数情况下是 Apache、Nginx、IIS、Tomcat 等 web 服务器），使用 HTTP 协议。服务器使用相同的协议来回答请求。

![A basic schema of the Web client/server architecture](/zh-CN/docs/Learn/Forms/Sending_and_retrieving_form_data/client-server.png)

在客户端，HTML 表单只不过是一种方便的用户友好的方式，可以配置 HTTP 请求将数据发送到服务器。这使用户能够提供在 HTTP 请求中传递的信息。

**备注：** 为了更好地了解客户端—服务器架构是如何工作的，请阅读我们的服务器端网站编程的第一个步骤模块。

### 在客户端：定义如何发送数据

`<form>`元素定义了如何发送数据。它的所有属性都是为了让你配置当用户点击提交按钮时发送的请求。两个最重要的属性是`action`和`method`。

#### `action` 属性

这个属性定义了发送数据要去的位置。它的值必须是一个有效的 URL。如果没有提供此属性，则数据将被发送到包含这个表单的页面的 URL。

在这个例子中，数据被发送到一个绝对 URL——`http://foo.com`：

```html
<form action="http://foo.com">…</form>
```

这里，我们使用相对 URL——数据被发送到服务器上的不同 URL

```html
<form action="/somewhere\_else">…</form>
```

在没有属性的情况下，像下面一样，`<form>`数据被发送到表单出现的相同页面上：

```html
<form>…</form>
```

许多较老的页面使用下面的符号表示数据应该被发送到包含表单的相同页面；这是必需的，因为直到 HTML5`action`属性都需要该符号。现在，这不再需要了。

```html
<form action="#">…</form>
```

**备注：** 可以指定使用 HTTPS(安全 HTTP) 协议的 URL。当你这样做时，数据将与请求的其余部分一起加密，即使表单本身是托管在使用 HTTP 访问的不安全页面上。另一方面，如果表单是在安全页面上托管的，但是你指定了一个不安全的 HTTP URL，它带有`action`属性，所有的浏览器都会在每次尝试发送数据时向用户显示一个安全警告，因为数据不会被加密。

#### `method` 属性

该属性定义了如何发送数据。HTTP 协议提供了几种执行请求的方法；HTML 表单数据可以通过许多不同的方法进行数据传输，其中最常见的是`GET`方法和`POST`方法。

 为了理解这两种方法之间的区别，让我们回过头来看看 HTTP 是如何工作的。
 每当你想要访问 Web 上的资源时，浏览器都会向 URL 发送一个请求。
 HTTP 请求由两个部分组成：一个包含关于浏览器功能的全局元数据集的头部，和一个包含服务器处理特定请求所需信息的主体。

##### GET 方法

`GET`方法是浏览器使用的方法，请求服务器返回给定的资源:“嘿，服务器，我想要得到这个资源。”在这种情况下，浏览器发送一个空的主体。由于主体是空的，如果使用该方法发送一个表单，那么发送到服务器的数据将被追加到 URL。

考虑下面这个表单：

```html
<form action="http://foo.com" method="get">
  <div>
    <label for="say">What greeting do you want to say?</label>
    <input name="say" id="say" value="Hi" />
  </div>
  <div>
    <label for="to">Who do you want to say it to?</label>
    <input name="to" id="to" value="Mom" />
  </div>
  <div>
    <button>Send my greetings</button>
  </div>
</form>
```

由于已经使用了`GET`方法，当你提交表单的时候，你将看到`www.foo.com/?say=Hi&to=Mom`在浏览器地址栏里。

![](/zh-CN/docs/Learn/Forms/Sending_and_retrieving_form_data/url-parameters.png)数据作为一系列的名称/值对被附加到 URL。在 URL web 地址结束之后，我们得到一个问号 (`?`)，后面跟着由一个与符号 (`&`) 互相分隔开的名称/值对。在本例中，我们将两个数据传递给服务器。

* `say`, 它有一个 `Hi`的值。
* `to`, 它有一个 `Mom`的值。

HTTP 请求如下：
```

GET /?say=Hi&to=Mom HTTP/2.0
Host: foo.com
```

**备注：** 你可以在 GitHub 上看到本例子——见 get-method.html (预览版).

##### POST 方法

`POST`方法略有不同。这是浏览器在询问响应时使用与服务器通信的方法，该响应考虑了 HTTP 请求正文中提供的数据:“嘿，服务器，看一下这些数据，然后给我回一个适当的结果。”如果使用该方法发送表单，则将数据追加到 HTTP 请求的主体中。

让我们来看一个例子，这是我们在上面的`GET`部分中所看到的相同的形式，但是使用`method`属性设置为`post`。

```html
<form action="http://foo.com" method="post">
  <div>
    <label for="say">What greeting do you want to say?</label>
    <input name="say" id="say" value="Hi" />
  </div>
  <div>
    <label for="to">Who do you want to say it to?</label>
    <input name="to" id="to" value="Mom" />
  </div>
  <div>
    <button>Send my greetings</button>
  </div>
</form>
```

当使用`POST`方法提交表单时，没有数据会附加到 URL，HTTP 请求看起来是这样的，而请求主体中包含的数据是这样的：
```

POST / HTTP/2.0
Host: foo.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 13

say=Hi&to=Mom
```

`Content-Length`数据头表示主体的大小，`Content-Type`数据头表示发送到服务器的资源类型。稍后我们将讨论这些标头。

**备注：** 你可以在 GitHub 上看到本例——见 post-method.html（预览版）。

#### 查看 HTTP 请求

HTTP 请求永远不会显示给用户 (如果你想要看到它们，你需要使用诸如Firefox Network Monitor或Chrome Developer Tools之类的工具)。例如，你的表单数据将显示在 Chrome 网络选项卡中：

1. 按下 F12
2. 选择 "Network"
3. 选择 "All"
4. 在 "Name" 标签页选择 "foo.com"
5. 选择 "Headers"

你可以获得表单数据，像下图显示的那样

![](/zh-CN/docs/Learn/Forms/Sending_and_retrieving_form_data/network-monitor.png)

唯一显示给用户的是被调用的 URL。正如我们上面提到的，使用`GET`请求用户将在他们的 URL 栏中看到数据，但是使用`POST`请求用户将不会看到。这一点很重要，有两个原因：

1. 如果你需要发送一个密码 (或其他敏感数据)，永远不要使用`GET`方法否则数据会在 URL 栏中显示，这将非常不安全。
2. 如果你需要发送大量的数据，那么`POST`方法是首选的，因为一些浏览器限制了 URL 的大小。此外，许多服务器限制它们接受的 URL 的长度。
### 在服务器端：检索数据

无论选择哪种 HTTP 方法，服务器都会接收一个字符串并解析，以便将数据作为键/值对序列获取。你访问这个序列的方式取决于你使用的开发平台以及你可能使用的任何特定框架。你使用的技术也决定了如何处理密钥副本；通常，最近收到的密钥的值是优先的。

#### 例如：原始 PHP

PHP提供了一些全局对象来访问数据。假设你已经使用了`POST`方法，那么下面的示例将获取数据并将其显示给用户。当然，你对数据的处理取决于你自己。你可以显示它，将它存储到数据库中，通过电子邮件发送它，或者以其他方式处理它。

```php
<?php
 // The global $\_POST variable allows you to access the data sent with the POST method by name
 // To access the data sent with the GET method, you can use $\_GET
 $say = htmlspecialchars($\_POST['say']);
 $to = htmlspecialchars($\_POST['to']);

 echo $say, ' ', $to;
?>
```

这个例子显示了一个带有我们发送的数据的页面。你可以在我们的示例php-example.html中看到这一点——该文件包含与我们之前看到的相同的示例表单，它使用了`post`的`method`和`php-example.php`的`action`。当提交时，它将表单数据发送到php-example.php，其中包含了上述代码块中所见的 php 代码。当执行此代码时，浏览器中的输出是`Hi Mom`。

![](/zh-CN/docs/Learn/Forms/Sending_and_retrieving_form_data/php-result.png)

**备注：** 当你将本例加载到本地浏览器中时，这个示例将无法工作---浏览器无法解析 PHP 代码，因此当提交表单时，浏览器只会为你提供下载 PHP 文件。为了让它生效，你需要通过某种类型的 PHP 服务器运行这个示例。本地 PHP 测试的好选择有MAMP(Mac 和 Windows) 和AMPPS(Mac、Windows、Linux)。

#### 例子：Python

 这个例子展示了如何使用 Python 完成同样的事情——在 web 页面上显示提交的数据。
 这将使用Flask framework来呈现模板、处理表单数据提交等 (参见python-example.py)。
```

from flask import Flask, render_template, request
app = Flask(__name__)

@app.route('/', methods=['GET', 'POST'])
def form():
    return render_template('form.html')

@app.route('/hello', methods=['GET', 'POST'])
def hello():
    return render_template('greeting.html', say=request.form['say'], to=request.form['to'])

if __name__ == "__main__":
    app.run()
```

以上代码中引用的两个模板如下：

* form.html: 与我们在 POST 方法 小节中看到的相同的表单，但是将`action`设置为`{{ url_for('hello') }}`。(这是一个Jinja2模板，它基本上是 HTML，但是可以包含对运行包含在花括号中的 web 服务器的 Python 代码的调用。`url_for('hello')`基本上是在“提交表单时重定向到`/hello`”。
* greeting.html: 这个模板只包含一行，用于呈现渲染时传递给它的两个数据块。
 这是通过前面所见的`hello()`函数完成的，该函数在`/hello`URL 被导向时运行。

**备注：** 同样，如果你只是尝试将其直接加载到浏览器中，那么这段代码将无法工作。Python 的工作方式与 PHP 略有不同——要在本地运行此代码，你需要安装 Python/pip，然后使用`pip3 install flask`安装 Flask。此时，你应该能够使用`python3 python-example.py`来运行这个示例，然后在浏览器中导航到`localhost:5000`。

#### 其他语言和框架

还有许多其他的服务器端技术可以用于表单处理，包括Perl、Java、 .Net、Ruby等。只挑你最喜欢的用就好。话虽如此，但值得注意的是，直接使用这些技术并不常见，因为这可能很棘手。更常见的是使用许多优秀的框架，这些框架使处理表单变得更容易，例如：

* Django for Python（比Flask要重量级一些，但是有更多的工具和选项。）
* Express for Node.js
* Laravel for PHP
* Ruby On Rails for Ruby
* Phoenix for Elixir

要注意的是，即使使用这些框架，使用表单也不一定很容易。但这比从头开始编写所有功能要简单得多，而且会节省很多时间。

**备注：** 向你介绍任何服务器端语言或框架超出了本文的范围。如果你想要学习这些它们，上面的链接会给你一些帮助。

特殊案例：发送文件
---------

用 HTML 表单发送文件是一个特殊的例子。文件是二进制数据——或者被认为是这样的——而所有其他数据都是文本数据。由于 HTTP 是一种文本协议，所以处理二进制数据有特殊的要求。

### `enctype` 属性

该属性允许你指定在提交表单时所生成的请求中的`Content-Type`的 HTTP 数据头的值。这个数据头非常重要，因为它告诉服务器正在发送什么样的数据。默认情况下，它的值是`application/x-www-form-urlencoded`。它的意思是：“这是已编码为 URL 参数的表单数据。”

如果你想要发送文件，你需要额外的三个步骤：

* 将`method`属性设置为`POST`，因为文件内容不能放入 URL 参数中。
* 将`enctype`的值设置为`multipart/form-data`，因为数据将被分成多个部分，每个文件单独占用一个部分，表单正文中包含的文本数据（如果文本也输入到表单中）占用一个部分。
* 包含一个或多个File picker小部件，允许用户选择将要上传的文件。

例如：

```html
<form method="post" enctype="multipart/form-data">
  <div>
    <label for="file">Choose a file</label>
    <input type="file" id="file" name="myFile" />
  </div>
  <div>
    <button>Send the file</button>
  </div>
</form>
```

**备注：** 一些浏览器支持`<input>`的`multiple`属性，它允许只用一个 `<input>` 元素选择一个以上的文件上传。服务器如何处理这些文件取决于服务器上使用的技术。如前所述，使用框架将使你的生活更轻松。

**警告：** 为了防止滥用，许多服务器配置了文件和 HTTP 请求的大小限制。在发送文件之前，先检查服务器管理员的权限是很重要的。

常见的安全问题
-------

每次向服务器发送数据时，都需要考虑安全性。到目前为止，HTML 表单是最常见的攻击路径 (可能发生攻击的地方)。这些问题从来都不是来自 HTML 表单本身，它们来自于服务器如何处理数据。

根据你所做的事情，你会遇到一些非常有名的安全问题：

### XSS 和 CSRF

跨站脚本 (XSS) 和跨站点请求伪造 (CSRF) 是常见的攻击类型，它们发生在当你将用户发送的数据显示给这个用户或另一个用户时。

XSS 允许攻击者将客户端脚本注入到其他用户查看的 Web 页面中。攻击者可以使用跨站点脚本攻击的漏洞来绕过诸如同源策略之类的访问控制。这些攻击的影响可能从一个小麻烦到一个重大的安全风险。

CSRF 攻击类似于 XSS 攻击，因为它们以相同的方式开始攻击——向 Web 页面中注入客户端脚本——但它们的目标是不同的。CSRF 攻击者试图将权限升级到特权用户 (比如站点管理员) 的级别，以执行他们不应该执行的操作 (例如，将数据发送给一个不受信任的用户)。

XSS 攻击利用用户对 web 站点的信任，而 CSRF 攻击则利用网站对其用户的信任。

为了防止这些攻击，你应该始终检查用户发送给服务器的数据 (如果需要显示)，尽量不要显示用户提供的 HTML 内容。相反，你应该对用户提供的数据进行处理，这样你就不会逐字地显示它。当今市场上几乎所有的框架都实现了一个最小的过滤器，它可以从任何用户发送的数据中删除 HTML`<script>`、`<iframe>` 和`<object>` 元素。这有助于降低风险，但并不一定会消除风险。

### SQL 注入

SQL 注入是一种试图在目标 web 站点使用的数据库上执行操作的攻击类型。这通常包括发送一个 SQL 请求，希望服务器能够执行它（通常发生在应用服务器试图存储由用户发送的数据时）。这实际上是攻击网站的主要途径之一。

其后果可能是可怕的，从数据丢失到通过使用特权升级控制整个网站基础设施的攻击。这是一个非常严重的威胁，你永远不应该存储用户发送的数据，而不执行一些清理工作 (例如，在 php/mysql 基础设施上使用`mysql_real_escape_string()`。

### HTTP 数据头注入和电子邮件注入

这种类型的攻击出现在当你的应用程序基于表单上用户的数据输入构建 HTTP 头部或电子邮件时。这些不会直接损害你的服务器或影响你的用户，但它们会引发一个更深入的问题，例如会话劫持或网络钓鱼攻击。

这些攻击大多是无声的，并且可以将你的服务器变成僵尸。

### 偏执：永远不要相信你的用户

那么，你如何应对这些威胁呢？这是一个远远超出本指南的主题，不过有一些规则需要牢记。最重要的原则是：永远不要相信你的用户，包括你自己；即使是一个值得信赖的用户也可能被劫持。

所有到达服务器的数据都必须经过检查和消毒。总是这样。没有例外。

* 远离有潜在危险的字符转义。应该如何谨慎使用的特定字符取决于所使用的数据的上下文和所使用的服务器平台，但是所有的服务器端语言都有相应的功能。
* 限制输入的数据量，只允许有必要的数据。
* 沙箱上传文件 (将它们存储在不同的服务器上，只允许通过不同的子域访问文件，或者通过完全不同的域名访问文件更好)。

如果你遵循这三条规则，你应该避免很多问题，但是如果你想要得到一个有能力的第三方执行的安全检查，这是一个好主意。不要以为你已经看到了所有可能的问题。

**备注：** 我们的服务器端学习主题的网站安全性文章更详细地讨论了上述威胁和潜在的解决方案。

结论
--

如你所见，发送表单数据很容易，但要确保应用程序的安全性是很棘手的。请记住，前端开发人员不是应该定义数据安全模型的人。是的，我们将看到，执行客户端数据验证 (en-US)是可能的，但是服务器不能信任这种验证，因为它无法真正知道客户端到底发生了什么。

参见
--

如果你想了解更多关于保护 web 应用程序的信息，你可以深入了解这些资源：

* Server-side website programming first steps
* The Open Web Application Security Project (OWASP)
* Chris Shiflett's blog about PHP Security
* 上一页
* Overview: Forms
* 下一页  (en-US)


其他表单控件 - 学习 Web 开发

其他表单控件
======

* 上一页
* Overview: Forms
* 下一页

现在我们来具体看看非 `<input>` 表单控件的功能，从下拉列表菜单（drop-down list）、多行文本域（multi-line text field）到其他有用的表单功能，如我们在前一篇文章看到的 `<output>` 元素和进度条（progress bar）元素。

|  |  |
| --- | --- |
| 前提： |
 计算机基础知识和对于
 HTML 的基本理解。
  |
| 目标： | 理解非 `<input>` 表单功能，以及如何使用 HTML 实现它们。 |

多行文本域
-----

多行文本域使用 `<textarea>` 元素指定，而不是使用 `<input>` 元素。

```html
<textarea cols="30" rows="8"></textarea>
```

这行代码的渲染效果如下所示：

`<textarea>` 元素和普通的单行文本域的主要区别是，用户可以在要提交的数据中包含硬换行（如按下回车键产生）。

`<textarea>` 也需要关闭标签，其中要包含的默认文字需要放在开闭标签之间。作为对比，`<input>` 是不包含关闭标签的空元素，它的默认值应该在 `value` 属性中指定。

注意，即使可以将任何东西放入到 `<textarea>` 元素中，甚至可以包含其他 HTML 元素、CSS 和 JavaScript，由于该元素的特性，这些内容都将以纯文本的形式渲染。在非表单组件上使用 `contenteditable` 可以为捕捉 HTML 或富文本内容提供 API。

在视觉上，输入的文字会换行，并且表单空间的大小是可调的。现代浏览器会提供拖动手柄，你可以通过拖拽来放大或缩小文本区的大小。

以下截图显示了 macOS 上 Firefox 71 和 Safari 13，Windows 10 上 Edge 18、Yandex 14、Firefox 71 和 Chrome 79 下的默认、聚焦和禁用的 `<textarea>` 元素。

![The default, focused, and disabled 'textarea' element in Firefox 71 and Safari 13 on Mac OSX and Edge 18, Yandex 14, Firefox and Chrome on Windows 10.](/zh-CN/docs/Learn/Forms/Other_form_controls/textarea_basic.png)

**备注：** 你可以在该系列的第一篇文章中整理的例子中找到一个稍微有趣的文本区域使用的示例（顺便看看源代码）。

### 控制多行渲染

`<textarea>` 接受三种属性来控制其多行渲染行为：

`cols`

指定文本控件的可见宽度（列），单位为字符的宽度。这实际上是起始宽度，因为它可以通过调整 `<textarea>` 的大小来改变，也可以用 CSS 重写。如果没有指定，默认值是 20。

`rows`

指定文本控件的可见行数。这实际上是起始高度，因为它可以通过调整 `<textarea>` 的大小来改变，也可以用 CSS 重写。如果没有指定，默认值是 2。

`wrap`

指定如何控制文本换行。取值可能为 `soft`（默认值），意味着提交的文字没有换行，而浏览器中渲染的文字有换行；`hard`（使用此属性必须指定 `cols` 的值），意味着提交的文字和浏览器中渲染的文字都有换行；和 `off`，停止任何换行行为。

### 控制文本域可缩放性

`<textarea>` 的缩放能力由 CSS `resize` 属性控制，其可能的值如下：

* `both`：默认值——允许横向和纵向缩放行为。
* `horizontal`：只允许横向水平缩放行为。
* `vertical`：只允许纵向竖直缩放行为。
* `none`：不允许缩放行为。
* `block` 和 `inline`：实验性的值，仅允许以 `block` 或 `inline` 方向缩放，如何变化取决于文字的方向，请参阅处理不同文字方向以了解更多内容。

多多调试 `resize` 参考页面顶部的例子，来看看它们是如何运行的。

下拉控件
----

下拉菜单是一种简单地让用户从许多不同选项中进行选择的控件，不占用用户界面太多空间。HTML 有两种下拉内容：一种是**选择框**、另外一种是**自动补全框**。这两种情况下的交互是相同的——一旦被激活，浏览器就会显示可供用户选择的值的列表。

**备注：** 你可以在 GitHub 的 drop-down-content.html 上找到所有下拉框类型的例子（也可以看看在线演示）。

### 选择框

一个简单的选择框是由 `<select>` 元素及一个或多个 `<option>` 子元素构成的，每个子元素指定了选择框的可能取值。

#### 基础示例

```html
<select id="simple" name="simple">
  <option>Banana</option>
  <option selected>Cherry</option>
  <option>Lemon</option>
</select>
```

如果需要的话，选择框的默认值可以由要指定默认值的 `<option>` 元素中的 `selected` 属性设置，这样在页面加载后，该选项可以预先选中。

#### 使用 optgroup

`<option>` 元素可以嵌套在 `<optgroup>` 元素中，以在视觉上关联一组取值：

```html
<select id="groups" name="groups">
  <optgroup label="fruits">
    <option>Banana</option>
    <option selected>Cherry</option>
    <option>Lemon</option>
  </optgroup>
  <optgroup label="vegetables">
    <option>Carrot</option>
    <option>Eggplant</option>
    <option>Potato</option>
  </optgroup>
</select>
```

在 `<optgroup>` 元素中，`label` 属性的值在嵌套选项之前显示。浏览器往往在视觉上将它们与选项分开（如，将其粗体并显示于不同的嵌套级别），以避免它们与实际选项混淆。

#### 使用 value 属性

如果一个 `<option>` 元素明确设置了 `value` 属性，当表单提交时也会提交那个选项对应的值。如果像上面的例子那样省略了 `value` 属性，`<option>` 元素的内容会作为提交的值。所以 `value` 属性并不是必需的，然而你可能需要向服务器中发送与视觉所见相比缩短或者改变过的值。

例如：

```html
<select id="simple" name="simple">
  <option value="banana">Big, beautiful yellow banana</option>
  <option value="cherry">Succulent, juicy cherry</option>
  <option value="lemon">Sharp, powerful lemon</option>
</select>
```

默认情况下，选择框的高度足以显示单个值。可选的 `size` (en-US) 属性控制在选择框不处于聚焦状态时，可见选项的数量。

### 多选选择框

默认情况下，选择框只允许用户选择单个值。通过向 `<select>` 元素添加 `multiple` 属性，你可以允许用户使用操作系统提供的机制选择多个值（如按下 `Cmd`/`Ctrl` 并先后单击多个值）。

```html
<select id="multi" name="multi" multiple size="2">
  <optgroup label="fruits">
    <option>Banana</option>
    <option selected>Cherry</option>
    <option>Lemon</option>
  </optgroup>
  <optgroup label="vegetables">
    <option>Carrot</option>
    <option>Eggplant</option>
    <option>Potato</option>
  </optgroup>
</select>
```

**备注：** 在多选选择框的情况下，你会注意到选择框不再以下拉内容的形式显示数值——相反，所有的值都会一次性显示在一个列表中，可选的 `size` (en-US) 属性决定了控件的高度。

**备注：** 任何支持 `<select>` 元素的浏览器也支持 `multiple` 属性。

### 自动补全框

你可以通过 `<datalist>` 元素中的一些显示的 `<option>` 子元素为表单项提供推荐的自动补全值。`<datalist>` 元素需要指定一个 `id`。

数据列表会使用 `list` 属性绑定至一个 `<input>` 元素（如 `text` 或 `email` 输入类型），该属性的取值就是要绑定的数据列表的 `id` 值。

一旦数据列表与表单控件相关联，它的选项就会被用来自动补全用户输入的文本；通常，它以下拉框的形式呈现给用户，列出他们输入内容的可能匹配。

#### 基础示例

我们来看一个例子。

```html
<label for="myFruit">What's your favorite fruit?</label>
<input type="text" name="myFruit" id="myFruit" list="mySuggestion" />
<datalist id="mySuggestion">
  <option>Apple</option>
  <option>Banana</option>
  <option>Blackberry</option>
  <option>Blueberry</option>
  <option>Lemon</option>
  <option>Lychee</option>
  <option>Peach</option>
  <option>Pear</option>
</datalist>
```

#### Datalist 支持和回退

几乎所有浏览器支持 datalist，但是如果你仍然需要给诸如 IE 10 及以前的旧版浏览器提供支持，有一个提供回退的小窍门：

```html
<label for="myFruit">What is your favorite fruit? (With fallback)</label>
<input type="text" id="myFruit" name="fruit" list="fruitList" />

<datalist id="fruitList">
  <label for="suggestion">or pick a fruit</label>
  <select id="suggestion" name="altFruit">
    <option>Apple</option>
    <option>Banana</option>
    <option>Blackberry</option>
    <option>Blueberry</option>
    <option>Lemon</option>
    <option>Lychee</option>
    <option>Peach</option>
    <option>Pear</option>
  </select>
</datalist>
```

支持 `<datalist>` 元素的浏览器会忽略任何非 `<option>` 元素，而 datalist 仍然会按期工作。不支持 `<datalist>` 元素的旧浏览器会显示标签和选择框。

以下屏幕截图显示了在 Safari 6 中 datalist 回退的渲染效果：

![Screenshot of the datalist element fallback with Safari on Mac OS](/zh-CN/docs/Learn/Forms/Other_form_controls/datalist-safari.png)

如果要使用这个回退处理，确保服务器端同时收集了 `<input>` 和 `<select>` 的值。

#### 不太明显的 datalist 用法

根据 HTML 规范，`list` 属性和 `<datalist>` 元素可以与任何需要用户输入的组件配合使用。这可能会导致一些不太显然的用法。

例如，在支持在 ``<datalist>`` 上使用 `range` 输入类型的浏览器中，在每个 datalist 的 ``<option>`` 值上会显示一个小勾。你可以在 `<input type="range">` 参考页面上看到一种实现。

支持 `<datalist>` 和 `<input type="color">` 的浏览器上应该显示一个自定义颜色调色板作为默认值，同时仍然提供完整的颜色调色板。

在这种情况下，不同的浏览器在不同的情况下表现不同，所以要把这种使用视为渐进式增强，并确保它们优雅地降级。

其他表单功能
------

还有一些其他的表单功能，不像我们已经提到的那样明显，但在某些情况下仍然有用，所以我们认为值得给它们一个简单的提及。

**备注：** 你可以在 GitHub 上找到本节的例子，如 other-examples.html（也可以看看在线演示）。

### 计量器和进度条

计量器和进度条是数值的视觉表现。

#### 进度条

进度条表示一个随时间变化的数值，最高可以达到 `max` 属性所指定的最大值。这样的条是使用 `<progress>` 元素创建出来的。

```html
<progress max="100" value="75">75/100</progress>
```

这可以实现任何需要进度报告的事情，例如下载文件的百分比，或问卷调查中填写过问题的数量。

`<progress>` 元素中的内容是供不支持此元素浏览器进行回退，以及给屏幕阅读器提供发音材料的机制。

#### 计量器

计量器代表了一个由 `max` 和 `min` 限定范围内的固定值。这个值在视觉上呈现为一个条形，为了了解这个条形的样子，我们将这个值与其他一些设定的值进行比较：

* `low` 和 `high` 将范围分为了三个部分：
    + 下半部分范围在 `min` 和 `low` 值之间，包含端点值。
    + 中间部分范围在 `low` 和 `high` 值之间，不包含端点值。
    + 上半部分范围在 `high` 和 `max` 值之间，包含端点值。
* `optimum` 值定义了 `<meter>` 元素的最佳值，它与 `low` 和 `high` 值一同定义了首选的范围部分：
    + 如果 `optimum` 在下半部分范围内，下半部分范围被认为是首选部分，中间部分范围被认为是平均部分，而上半部分范围被认为是最差的部分。
    + 如果 `optimum` 在中间部分范围内，下半部分范围被认为是平均部分，中间部分范围被认为是首选部分，而上半部分范围也被认为是平均部分。
    + 如果 `optimum` 在上半部分范围内，下半部分范围被认为是最差的部分，中间部分范围被认为是平均部分，而上半部分范围被认为是首选部分。

所有实现了 `<meter>` 元素的浏览器使用这些值来改变计量器的颜色：

* 如果当前值位于首选范围，则计量器显示为绿色。
* 如果当前值位于平均范围，则计量器显示为黄色。
* 如果当前值位于最差范围，则计量器显示为红色。

像下面这种条形是使用 `<meter>` 元素创建出来的。这是为了实现任何类型的仪表，例如，显示磁盘上使用的总空间的条形图，当它开始变满时就变成红色。

```html
<meter min="0" max="100" value="75" low="33" high="66" optimum="50">75</meter>
```

`<meter>` 元素中的内容是供不支持此元素浏览器进行回退，以及给屏幕阅读器提供发音材料的机制。

能支持 `<progress>` 和 `<meter>` 已经非常不错了——除了 Internet Explorer 不支持，其他浏览器都可以支持。

技能测试！
-----

你已经看完了这篇文章的主要内容，但你还记得所有重要的信息吗？在继续学习其他内容之前，你可以在这里找到进一步的测试来确定你是否掌握了这些知识点——请参见技能测试：其他控件 (en-US)。

总结
--

正如你在过去的几篇文章中所看到的，有许多不同类型的表单控件。你不需要一下子记住所有这些细节，只要你愿意，可以经常回到这些文章中来检查细节。

现在你已经掌握了不同的可用表单控件背后的 HTML 代码，接下来我们将看看如何进行样式化表单。

* 上一页
* Overview: Forms
* 下一页
### 进阶内容

* 如何构建表单控件
* 使用 JavaScript 发送表单
* 表单控件兼容性列表


表单数据校验 - 学习 Web 开发

表单数据校验
======

* 上一页
* Overview: Forms
* 下一页

表单校验帮助我们确保用户以正确格式填写表单数据，确保提交的数据能使我们的应用程序正常工作。本文将告诉你需要了解的有关表单校验的内容。

|  |  |
| --- | --- |
| 前提： |
 计算机基础能力，对 HTML、CSS
 和 JavaScript 有一定的理解。
  |
| 目标： | 理解表单校验是什么，为什么它很重要，以及如何实现它。 |

什么是表单数据校验？
----------

访问任何一个带注册表单的网站，你都会发现，当你提交了没有输入符合预期格式的信息的表单时，注册页面都会给你一个反馈，这些信息可能看起来像下面这样的：

* “该字段是必填的”（该字段不能留空）
* “请输入你的电话号码，它的格式是：xxx-xxxx”（它要求你输入的数据格式为三个数字接一个横杠，然后再接着是四个数字）
* “请输入一个合法的邮箱地址”（如果你输入的数据不符合“somebody@example.com“的邮箱格式）
* “你的密码长度应该是 8 至 30 位的，并且至少应该包含一个大写字母、一个符号以及一个数字”

这就是**表单校验**——当你向 Web 应用输入数据时，应用会验证你输入的数据是否是正确的。如果验证通过，应用允许提交这些数据到服务器并储存到数据库中（通常情况下），如果验证未通过，则 Web 应用会提示你有错误的数据，并且一般都会明确的告诉你错误发生在哪里。表单校验可以通过许多不同的方式实现。

**备注：** 下面一段在英文原文中已经删除

（事实上，没有人愿意填写表单——很多证据表明，用户对填写表单这件事情都感到很烦恼，如果他们在填写表单的过程中遇到一些自己无法理解的问题，通常都会导致他们直接离开你的 Web 应用，简而言之，表单是一个很烦人的东西。）

我们希望把填写表单变的越简单越好。那么，为什么我们还坚持进行表单的数据校验呢？这有三个最主要的原因：

* **我们希望以正确的格式获取到正确的数据**——如果我们的用户数据以不正确的格式存储，或者他们没有输入正确的信息/完全省略信息，我们的应用程序将无法正常运行。
* **我们希望保护我们的用户**——强制用户输入安全的密码，有利于保护他们的账户信息。
* **我们希望保护我们自己**——恶意用户有很多通过滥用应用中缺乏保护的表单破坏应用的方法（具体请参见网站安全）。

**警告：** 永远不要相信从客户端传递到服务器的数据。即使你的表单正确验证并防止输入格式错误，恶意用户仍然可以更改网络请求。

### 不同类型的表单数据校验

在 Web 中，你可能会遇见各种不同的表单校验：

* **客户端校验**发生在浏览器端，表单数据被提交到服务器之前，这种方式相较于服务器端校验来说，用户体验更好，它能实时的反馈用户的输入校验结果，这种类型的校验可以进一步细分成下面这些方式：
    + **JavaScript** 校验，这是可以完全自定义的实现方式；
    + HTML5 **内置校验**，这不需要 JavaScript，而且性能更好，但是不能像 JavaScript 那样可自定义。
* **服务器端校验**则是发生在浏览器提交数据并被服务器端程序接收之后——通常服务器端校验都是发生在将数据写入数据库之前，如果数据没通过校验，则会直接从服务器端返回错误消息，并且告诉浏览器端发生错误的具体位置和原因，服务器端校验不像客户端校验那样有好的用户体验，因为它直到整个表单都提交后才能返回错误信息。但是服务器端校验是你的应用对抗错误/恶意数据的最后防线，在这之后，数据将被持久化至数据库。当今所有的服务端框架都提供了数据**校验**与**清洁**功能（让数据更安全）。

在真实的项目开发过程中，开发者一般都倾向于使用客户端校验与服务器端校验的组合校验方式以更好的保证数据的正确性与安全性。

使用内置表单数据校验
----------

HTML5 一个特别有用的新功能就是，可以在不写一行脚本代码的情况下，即对用户的输入进行数据校验，这都是通过表单元素的校验属性 (en-US)实现的，这些属性可以让你定义一些规则，用于限定用户的输入，比如某个输入框是否必须输入，或者某个输入框的字符串的最小最大长度限制，或者某个输入框必须输入一个数字、邮箱地址等；还有数据必须匹配的模式。如果表单中输入的数据都符合这些限定规则，那么表示这个表单校验通过，否则则认为校验未通过。

当一个元素校验通过时：

* 该元素将可以通过 CSS 伪类 `:valid` 进行特殊的样式化；
* 如果用户尝试提交表单，如果没有其他的控制来阻止该操作（比如 JavaScript 即可阻止提交），那么该表单的数据会被提交。

如果一个元素未校验通过：

* 该元素将可以通过 CSS 伪类 `:invalid` 进行特殊的样式化；
* 如果用户尝试提交表单，浏览器会展示出错误消息，并停止表单的提交。
### input 元素的校验约束 — starting simple

在这一节，我们将会看到一些用于`<input>`元素校验的 HTML5 的特性。

让我们用一个简单的例子开始 — 一个可以让你从香蕉或樱桃中选择你最喜欢的水果的 input。这个包含了一个简单的文本`<input>` 和一个与之匹配的 label，还有一个 submit `<button>`。你可以在 GitHub fruit-start.html找到源码，在线例子如下：
```

<form>
  <label for="choose">Would you prefer a banana or cherry?</label>
  <input id="choose" name="i\_like" />
  <button>Submit</button>
</form>
```

```
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}
```

开始之前，先拷贝一份 `fruit-start.html` 放在你硬盘上的新目录里。

### required 属性

最简单的 HTML5 校验功能是 `required`属性 — 如果要使输入成为必需的，则可以使用此属性标记元素。当设置此属性时，如果输入为空，该表单将不会提交（并将显示错误消息），输入也将被视为无效。

添加一个 `required` 属性到你的 input 元素，如下所示：

```html
<form>
  <label for="choose">Would you prefer a banana or cherry?</label>
  <input id="choose" name="i\_like" required />
  <button>Submit</button>
</form>
```

同时注意在示例文件中包含的 CSS :

```css
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}
```

以上样式效果为：在校验失败时 输入框会有一个亮红色的虚线边框，在校验通过时会有一个更微妙的黑色边框。在以下示例中尝试新的行为：

### 使用正则表达式校验

另一个常用的校验功能是 `pattern` 属性，以 Regular Expression 作为 value 值。正则表达式 (regex) 是一个可以用来匹配文本字符串中字符的组合的模式，所以它们是理想的表单校验器，也可以支持 JavaScript 中许多其他的用途。

正则表达式相当复杂，我们不打算在本文中详尽地教你。

下面是一些例子，让你对它们的工作原理有个基本的了解：

* `a` — 匹配一个字符`a`(不能匹配 `b`, `aa`等等.)
* `abc` — 匹配 `a`、其次 `b`、最后 `c`.
* `a*` — 匹配 0 个或者多个字符 `a` (`+` 代表至少匹配一个或者多个).
* `[^a]` — 匹配一个字符，但它**不能**是`a`.
* `a|b` — 匹配一个字符 `a` 或者 `b`.
* `[abc]` — 匹配一个字符，它可以是`a`、`b`或`c`.
* `[^abc]` — 匹配一个字符，但它**不可以**是 `a`、`b` 或 `c`.
* `[a-z]` — 匹配字符范围 `a-z`且全部小写 (你可以使用 `[A-Za-z]` 涵盖大小写，或 `[A-Z]` 来限制必须大写).
* `a.c`——匹配字符 `a`，中间匹配任意一个字符，最后匹配字符 `c`。
* `a{5}` — 匹配字符 `a` 五次。
* `a{5,7}` — 匹配字符 `a` 五到七次，不能多或者少。

你也可以在这些表达式中使用数字和其他字符，例如：

* `[ -]` — 匹配一个空格或者虚线。
* `[0-9]` — 匹配数字范围 0~9.

你可以任意地组合这些，你可以任意指定不同的部分：

* `[Ll].*k` — 匹配一个大写`L`或者小写的`l`, 之后匹配 0 个或多个任意类型的字符，最后匹配一个小写字母 k.
* `[A-Z][A-Za-z' -]+` — 一个大写字母后面跟着匹配一个及以上的大小写字母或者中划线或者撇号或者空格。这个可以用于校验英语会话中城市或城镇名，但这需要首字母以大写开头，不包括其他字符，例如来自英国的 Manchester, Ashton-under-lyne, 以及 Bishop's Stortford 等。
* `[0-9]{3}[ -][0-9]{3}[ -][0-9]{4}` — 简单的匹配一个美国内的电话号码 — 三个数字 0`-`9, 后面跟着一个空格或者中划线，之后匹配三个数字 0`-`9, 再跟着一个空格或者中划线，最后跟着四个数字 0`-`9. 但实际情况可能更加复杂，因为有些人会给号码加上括号什么的，这里的表达式只是用来做一个简单的演示。

不管怎么说，让我们来实现这些例子 — 更新你的 html 文档表单增加一个 `pattern` 属性，如下：

```html
<form>
  <label for="choose">Would you prefer a banana or a cherry?</label>
  <input id="choose" name="i\_like" required pattern="banana|cherry" />
  <button>Submit</button>
</form>
```

```
input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}
```

这个例子中，该 `<input>` 元素接受两个值中的一个：字符串 "banana" 或者字符串"cherry".

在这个基础上，尝试把`pattern` 属性内部的表达式改变成上面的几个例子，然后看看这些表达式如何影响你可以输入的值以使输入值有效。尝试写一些你自己设计的，看看它如何工作。尽量让他们与水果有关这样你的例子才会有意义。

**备注：** 一些 `<input>` 元素类型不需要`pattern` 属性进行校验。指定特定 `email` 类型 就会使用匹配电子邮件格式的正则表达式来校验 (如果有 `multiple` 属性请用逗号来分割多个邮箱). 进一步来说，字段 `url` 类型则会自动校验输入的是否为一个合法的链接。

**备注：** 该 `<textarea>` 元素不支持`pattern` 属性。

### 限制输入的长度

所有文本框 (`<input>` 或 `<textarea>`) 都可以使用`minlength` 和 `maxlength` 属性来限制长度。如果输入的字段长度小于 `minlength` 的值或大于 `maxlength` 值则无效。浏览器通常不会让用户在文本字段中键入比预期更长的值，不过更精细的设置总归是更好的。

在数字条目中 (i.e. `<input type="number">`), 该 `min` 和 `max` 属性同样提供校验约束。如果字段的值小于`min` 属性的值或大于 `max` 属性的值，该字段则无效。

让我来看看另外一个例子。创建一个 fruit-start.html 文件副本。

现在删除 `<body>` 元素中的内容，替换成下面的代码：

```html
<form>
  <div>
    <label for="choose">Would you prefer a banana or a cherry?</label>
    <input id="choose" name="i\_like" required minlength="6" maxlength="6" />
  </div>
  <div>
    <label for="number">How many would you like?</label>
    <input type="number" id="number" name="amount" value="1" min="1" max="10" />
  </div>
  <div>
    <button>Submit</button>
  </div>
</form>
```

* 这里我们看到 `text` 条目的属性`minlength` 和 `maxlength` 都为 6 — 这 banana 和 cherry 的长度都为 6. 输入少于这个长度的字符显示无效，大多浏览器不能输入超过该限制的长度的字符。
* 我们同时也能让 `number` 条目数值限制在 `min` 为 1 和 一个 `max` 为 10 中 — 输入超出范围则显示无效，并且你将无法使用递增/递减箭头将该值改变到此范围之外。
```

input:invalid {
  border: 2px dashed red;
}

input:valid {
  border: 2px solid black;
}

div {
  margin-bottom: 10px;
}
```

这里是运行的例子：

**备注：** `<input type="number">` (或者其他类型，像 `range`) 也可以获取到一个`step` 属性，指定了值在增减过程固定改变的值 (如向上增加和向下减少的按钮).

### 完整的例子

这里就是一个完整的展示 HTML 中使用校验属性的例子：

```html
<form>
  <p>
    <fieldset>
      <legend>Title<abbr title="This field is mandatory">*</abbr></legend>
      <input type="radio" required name="title" id="r1" value="Mr"><label for="r1">Mr.</label>
      <input type="radio" required name="title" id="r2" value="Ms"><label for="r2">Ms.</label>
    </fieldset>
  </p>
  <p>
    <label for="n1">How old are you?</label>
    <!-- 这里的 pattern 属性可以用作不支持 number 类 input 浏览器的备用方法
 请注意当与数字输入框一起使用时，支持 pattern 属性的浏览器会使它沉默失效。
 它仅仅是在这里用作备用 -->
    <input type="number" min="12" max="120" step="1" id="n1" name="age"
 pattern="\d+">
  </p>
  <p>
    <label for="t1">What's your favorite fruit?<abbr title="This field is mandatory">*</abbr></label>
    <input type="text" id="t1" name="fruit" list="l1" required
 pattern="[Bb]anana|[Cc]herry|[Aa]pple|[Ss]trawberry|[Ll]emon|[Oo]range">
    <datalist id="l1">
      <option>Banana</option>
      <option>Cherry</option>
      <option>Apple</option>
      <option>Strawberry</option>
      <option>Lemon</option>
      <option>Orange</option>
    </datalist>
  </p>
  <p>
    <label for="t2">What's your e-mail?</label>
    <input type="email" id="t2" name="email">
  </p>
  <p>
    <label for="t3">Leave a short message</label>
    <textarea id="t3" name="msg" maxlength="140" rows="5"></textarea>
  </p>
  <p>
    <button>Submit</button>
  </p>
</form>
```

```css
body {
  font: 1em sans-serif;
  padding: 0;
  margin: 0;
}

form {
  max-width: 200px;
  margin: 0;
  padding: 0 5px;
}

p > label {
  display: block;
}

input[type="text"],
input[type="email"],
input[type="number"],
textarea,
fieldset {
  /\* 需要在基于 WebKit 的浏览器上对表单元素进行恰当的样式设置 \*/
  -webkit-appearance: none;

  width: 100%;
  border: 1px solid #333;
  margin: 0;

  font-family: inherit;
  font-size: 90%;

  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

input:invalid {
  box-shadow: 0 0 5px 1px red;
}

input:focus:invalid {
  outline: none;
}
```

### 自定义错误信息

正如我们上面所看到的例子，每次我们提交无效的表单数据时，浏览器总会显示错误信息。但是显示的信息取决于你所使用的浏览器。

这些自动生成的错误有两个缺点：

* 没有标准可以让 CSS 来改变他们的界面外观。
* 这依赖于他们使用的浏览器环境，意味着你可能在这种语言的页面里得到另一种语言的错误提示。

![Example of an error message with Firefox in French on an English page](/zh-CN/docs/Learn/Forms/Form_validation/error-firefox-win7.png)

要自定义这些消息的外观和文本，你必须使用 JavaScript; 不能使用 HTML 和 CSS 来改变。

HTML5 提供 constraint validation API 来检测和自定义表单元素的状态。除此之外，他可以改变错误信息的文本。让我们快速的看一个例子：

```html
<form>
  <label for="mail">I would like you to provide me an e-mail</label>
  <input type="email" id="mail" name="mail" />
  <button>Submit</button>
</form>
```

在 JavaScript 中，你调用 `setCustomValidity()` (en-US) 方法：

```js
var email = document.getElementById("mail");

email.addEventListener("input", function (event) {
  if (email.validity.typeMismatch) {
    email.setCustomValidity("I expect an e-mail, darling!");
  } else {
    email.setCustomValidity("");
  }
});
```

使用 JavaScript 校验表单
------------------

如果你想控制原生错误信息的界面外观，或者你想处理不支持 HTML 内置表单校验的浏览器，则必须使用 Javascript。

### 约束校验的 API

越来越多的浏览器支持限制校验 API，并且这逐渐变得可靠。这些 API 由成组的方法和属性构成，可在特定的表单元素接口上调用：

* HTMLButtonElement
* HTMLFieldSetElement
* HTMLInputElement
* HTMLOutputElement (en-US)
* HTMLSelectElement (en-US)
* HTMLTextAreaElement

#### 约束校验的 API 及属性

| 属性 | 描述 |
| --- | --- |
| `validationMessage` | 一个本地化消息，描述元素不满足校验条件时（如果有的话）的文本信息。如果元素无需校验（`willValidate` 为 `false`），或元素的值满足校验条件时，为空字符串。 |
| `validity` | 一个 `ValidityState` 对象，描述元素的验证状态。详见有关可能的验证状态的文章。 |
| `validity.customError` | 如果元素设置了自定义错误，返回 `true` ；否则返回`false`。 |
| `validity.patternMismatch` | 如果元素的值不匹配所设置的正则表达式，返回 `true`，否则返回 `false`。当此属性为 `true` 时，元素将命中 `:invalid` CSS 伪类。 |
| `validity.rangeOverflow` | 如果元素的值高于所设置的最大值，返回 `true`，否则返回 `false`。当此属性为 `true` 时，元素将命中 `:invalid` CSS 伪类。 |
| `validity.rangeUnderflow` | 如果元素的值低于所设置的最小值，返回 `true`，否则返回 `false`。当此属性为 `true` 时，元素将命中 `:invalid` CSS 伪类。 |
| `validity.stepMismatch` | 如果元素的值不符合 step 属性的规则，返回 `true`，否则返回 `false`。当此属性为 `true` 时，元素将命中 `:invalid` CSS 伪类。 |
| `validity.tooLong` | 如果元素的值超过所设置的最大长度，返回 `true`，否则返回 `false`。当此属性为 `true` 时，元素将命中 `:invalid` CSS 伪类。 |
| `validity.typeMismatch` | 如果元素的值出现语法错误，返回 `true`，否则返回 `false`。当此属性为 `true` 时，元素将命中 `:invalid` CSS 伪类。 |
| `validity.valid` | 如果元素的值不存在校验问题，返回 `true`，否则返回 `false`。当此属性为 `true` 时，元素将命中 `:valid` CSS 伪类，否则命中 `:invalid` CSS 伪类。 |
| `validity.valueMissing` | 如果元素设置了 required 属性且值为空，返回 `true`，否则返回 `false`。当此属性为 true 时，元素将命中 `:invalid` CSS 伪类。 |
| `willValidate` | 如果元素在表单提交时将被校验，返回 `true`，否则返回 `false`。 |

#### 约束校验 API 的方法

| 方法 | 描述 |
| --- | --- |
| `checkValidity()` |
 如果元素的值不存在校验问题，返回 `true`，否则返回
 `false`。如果元素校验失败，此方法会触发 `invalid` 事件。
  |
| `HTMLFormElement.reportValidity()` |
 如果元素或它的子元素控件符合校验的限制，返回 `true` .
 当返回为 `false` 时，对每个无效元素可撤销
 `invalid` 事件会被唤起并且校验错误会报告给用户。
  |
| `setCustomValidity(*message*)` |
 为元素添加一个自定义的错误消息；如果设置了自定义错误消息，该元素被认为是无效的，则显示指定的错误。这允许你使用
 JavaScript 代码来建立校验失败，而不是用标准约束校验 API
 所提供的。这些自定义信息将在向用户报告错误时显示。如果参数为空，则清空自定义错误。
  |

对于旧版浏览器，可以使用 polyfill（例如 Hyperform），来弥补其对约束校验 API 支持的不足。既然你已经使用 JavaScript，在你的网站或 Web 应用程序的设计和实现中使用 polyfill 并不是累赘。

#### 使用约束校验 API 的例子

让我们看看如何使用这个 API 来构建自定义错误消息。首先，HTML：

```html
<form novalidate>
  <p>
    <label for="mail">
      <span>Please enter an email address:</span>
      <input type="email" id="mail" name="mail" />
      <span class="error" aria-live="polite"></span>
    </label>
  </p>
  <button>Submit</button>
</form>
```

这个简单的表单使用 `novalidate` 属性关闭浏览器的自动校验；这允许我们使用脚本控制表单校验。但是，这并不禁止对约束校验 API 的支持或是以下 CSS 伪类：`:valid`、`:invalid`、`:in-range` 、`:out-of-range` 的应用。这意味着，即使浏览器在发送数据之前没有自动检查表单的有效性，你仍然可以自己做，并相应地设置表单的样式。

`aria-live` (en-US) 属性确保我们的自定义错误信息将呈现给所有人，包括使用屏幕阅读器等辅助技术的人。

##### CSS

以下 CSS 样式使我们的表单和其错误输出看起来更有吸引力。

```css
/\* 仅为了使示例更好看 \*/
body {
  font: 1em sans-serif;
  padding: 0;
  margin: 0;
}

form {
  max-width: 200px;
}

p \* {
  display: block;
}

input[type="email"] {
  -webkit-appearance: none;

  width: 100%;
  border: 1px solid #333;
  margin: 0;

  font-family: inherit;
  font-size: 90%;

  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

/\* 校验失败的元素样式 \*/
input:invalid {
  border-color: #900;
  background-color: #fdd;
}

input:focus:invalid {
  outline: none;
}

/\* 错误消息的样式 \*/
.error {
  width: 100%;
  padding: 0;

  font-size: 80%;
  color: white;
  background-color: #900;
  border-radius: 0 0 5px 5px;

  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

.error.active {
  padding: 0.3em;
}
```

##### JavaScript

以下 JavaScript 代码演示如何设置自定义错误校验。

```js
// 有许多方式可以获取 DOM 节点；在此我们获取表单本身和
// email 输入框，以及我们将放置错误信息的 span 元素。

var form = document.getElementsByTagName("form")[0];
var email = document.getElementById("mail");
var error = document.querySelector(".error");

email.addEventListener(
  "input",
  function (event) {
    // 当用户输入信息时，校验 email 字段
    if (email.validity.valid) {
      // 如果校验通过，清除已显示的错误消息
      error.innerHTML = ""; // 重置消息的内容
      error.className = "error"; // 重置消息的显示状态
    }
  },
  false,
);
form.addEventListener(
  "submit",
  function (event) {
    // 当用户提交表单时，校验 email 字段
    if (!email.validity.valid) {
      // 如果校验失败，显示一个自定义错误
      error.innerHTML = "I expect an e-mail, darling!";
      error.className = "error active";
      // 还需要阻止表单提交事件，以取消数据传送
      event.preventDefault();
    }
  },
  false,
);
```

这是运行结果：

约束校验 API 为你提供了一个强大的工具来处理表单校验，让你可以对用户界面进行远超过仅仅使用 HTML 和 CSS 所能得到的控制。

### 不使用内建 API 时的表单校验

有时，例如使用旧版浏览器或自定义小部件 (en-US)，你将无法（或不希望）使用约束校验 API。在这种情况下，你仍然可以使用 JavaScript 来校验你的表单。校验表单比起真实数据校验更像是一个用户界面问题。

要校验表单，你必须问自己几个问题：

我应该进行什么样的校验？

你需要确定如何校验你的数据：字符串操作，类型转换，正则表达式等。这取决于你。只要记住，表单数据一般都是文本，并总是以字符串形式提供给脚本。

如果表单校验失败，我该怎么办？

这显然是一个 UI 问题。你必须决定表单的行为方式：表单是否发送数据？是否突出显示错误的字段？是否显示错误消息？

如何帮助用户纠正无效数据？

为了减少用户的挫折感，提供尽可能多的有用的信息是非常重要的，以便引导他们纠正他们的输入。你应该提供前期建议，以便他们知道预期的输入是什么以及明确的错误消息。如果你想深入了解表单校验用户界面要求，那么你应该阅读一些有用的文章：

* SmashingMagazine: Form-Field Validation: The Errors-Only Approach
* SmashingMagazine: Web Form Validation: Best Practices and Tutorials
* Six Revision: Best Practices for Hints and Validation in Web Forms
* A List Apart: Inline Validation in Web Forms

#### 不使用约束校验 API 的例子

为了说明这一点，让我们重构一下前面的例子，以便它可以在旧版浏览器中使用：

```html
<form>
  <p>
    <label for="mail">
      <span>Please enter an email address:</span>
      <input type="text" class="mail" id="mail" name="mail" />
      <span class="error" aria-live="polite"></span>
    </label>
  </p>

  <p>
    <!-- Some legacy browsers need to have the `type` attribute
 explicitly set to `submit` on the `button`element -->
    <button type="submit">Submit</button>
  </p>
</form>
```

正如你所看到的，HTML 几乎是一样的；我们只是关闭了 HTML 校验功能。请注意，ARIA (en-US) 是与 HTML5 无关的独立规范。

##### CSS

同样的，CSS 也不需要太多的改动，我们只需将 `:invalid` 伪类变成真实的类，并避免使用不适用于 Internet Explorer 6 的属性选择器。

```css
/\* 仅为了使示例更好看 \*/
body {
  font: 1em sans-serif;
  padding: 0;
  margin: 0;
}

form {
  max-width: 200px;
}

p \* {
  display: block;
}

input.mail {
  -webkit-appearance: none;

  width: 100%;
  border: 1px solid #333;
  margin: 0;

  font-family: inherit;
  font-size: 90%;

  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

/\* 校验失败的元素样式 \*/
input.invalid {
  border-color: #900;
  background-color: #fdd;
}

input:focus.invalid {
  outline: none;
}

/\* 错误消息的样式 \*/
.error {
  width: 100%;
  padding: 0;

  font-size: 80%;
  color: white;
  background-color: #900;
  border-radius: 0 0 5px 5px;

  -moz-box-sizing: border-box;
  box-sizing: border-box;
}

.error.active {
  padding: 0.3em;
}
```

##### JavaScript

JavaScript 代码有很大的变化，需要做更多的工作。

```js
// 使用旧版浏览器选择 DOM 节点的方法较少
var form = document.getElementsByTagName("form")[0];
var email = document.getElementById("mail");

// 以下是在 DOM 中访问下一个兄弟元素的技巧
// 这比较危险，很容易引起无限循环
// 在现代浏览器中，应该使用 element.nextElementSibling
var error = email;
while ((error = error.nextSibling).nodeType != 1);

// 按照 HTML5 规范
var emailRegExp =
  /^[a-zA-Z0-9.!#$%&'\*+/=?^\_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)\*$/;

// 许多旧版浏览器不支持 addEventListener 方法
// 这只是其中一种简单的处理方法
function addEvent(element, event, callback) {
  var previousEventCallBack = element["on" + event];
  element["on" + event] = function (e) {
    var output = callback(e);

    // 返回 `false` 来停止回调链，并中断事件的执行
    if (output === false) return false;

    if (typeof previousEventCallBack === "function") {
      output = previousEventCallBack(e);
      if (output === false) return false;
    }
  };
}

// 现在我们可以重构字段的约束校验了
// 由于不使用 CSS 伪类，我们必须明确地设置 valid 或 invalid 类到 email 字段上
addEvent(window, "load", function () {
  // 在这里验证字段是否为空（请记住，该字段不是必需的）
  // 如果非空，检查它的内容格式是不是合格的 e-mail 地址
  var test = email.value.length === 0 || emailRegExp.test(email.value);

  email.className = test ? "valid" : "invalid";
});

// 处理用户输入事件
addEvent(email, "input", function () {
  var test = email.value.length === 0 || emailRegExp.test(email.value);
  if (test) {
    email.className = "valid";
    error.innerHTML = "";
    error.className = "error";
  } else {
    email.className = "invalid";
  }
});

// 处理表单提交事件
addEvent(form, "submit", function () {
  var test = email.value.length === 0 || emailRegExp.test(email.value);

  if (!test) {
    email.className = "invalid";
    error.innerHTML = "I expect an e-mail, darling!";
    error.className = "error active";

    // 某些旧版浏览器不支持 event.preventDefault() 方法
    return false;
  } else {
    email.className = "valid";
    error.innerHTML = "";
    error.className = "error";
  }
});
```

该结果如下：

正如你所看到的，建立自己的校验系统并不难。困难的部分是使其足够通用，以跨平台和任何形式使用它可以创建。有许多库可用于执行表单校验; 你应该毫不犹豫地使用它们。这里有一些例子：

* 独立的库（原生 Javascript 实现）：
    + Validate.js
* jQuery 插件：
    + Validation
    + Valid8

#### 远程校验

在某些情况下，执行一些远程校验可能很有用。当用户输入的数据与存储在应用程序服务器端的附加数据绑定时，这种校验是必要的。一个应用实例就是注册表单，在这里你需要一个用户名。为了避免重复，执行一个 AJAX 请求来检查用户名的可用性，要比让先用户发送数据，然后因为表单重复了返回错误信息要好得多。

执行这样的校验需要采取一些预防措施：

* 它要求公开 API 和一些数据；你需要确保它不是敏感数据。
* 网络滞后需要执行异步校验。这需要一些用户界面的工作，以确保如果校验没有适当的执行，用户不会被阻止。
结论
--

表单校验并不需要复杂的 JavaScript，但它需要对用户的仔细考虑。一定要记住帮助你的用户更正他提供的数据。为此，请务必：

* 显示明确的错误消息。
* 放宽输入格式限制。
* 指出错误发生的位置（特别是在大型表单中）。
* 上一页
* Overview: Forms
* 下一页


高级设计 HTML 表单 - 学习 Web 开发

高级设计 HTML 表单
============

* 上一页
* Overview: Forms
* 下一页

在本文中，我们将看到HTML表单怎样使用CSS装饰难以定制的表单小部件。如前面章节 (en-US)所示，文本域和按钮完全可以使用 CSS，现在我们将深入探索 HTML 表单样式。

在继续之前，让我们回忆一下两种表单小部件：

bad

这个元素很难设计，需要一些复杂的技巧，有时还涉及到高级的 CSS3 的知识。

ugly

忘记使用 CSS 来设计这些元素吧。你最多能做一点点事情，还不能保证可以跨浏览器，而且在它们出现时永远不能做到完全的受控。

CSS 表现力
-------

除了文本框和按钮之外，使用其他表单小部件的主要问题是在许多情况下，CSS 的表现不能满足设计复杂的小部件的要求。

HTML 和 CSS 最新的发展扩展了 CSS 的表现力：

* CSS 2.1 非常受限，只给出三个伪类：
    + `:active`
    + `:focus`
    + `:hover`
* CSS Selector Level 3 增加了三个与 HTML 表单相关的伪类：
    + `:enabled`
    + `:disabled`
    + `:checked`
    + `:indeterminate`
* CSS Basic UI Level 3 也增加了几个伪类用于描述小部件的状态：
    + `:default`
    + `:valid`
    + `:invalid`
    + `:in-range`
    + `:out-of-range`
    + `:required`
    + `:optional`
    + `:read-only`
    + `:read-write`
* CSS Selector Level 4 目前处于积极应用和重点讨论的状态，但并不打算为表单做更多的改善：
    + `:user-error` 只是改进了伪类`:invalid`。

所有这一切是一个好的开端，但是有两个问题。首先，一些浏览器不需要实现 CSS 2.1 之上的特性。其次在设计像日期选择器这样的复杂的小部件时，这些实在不够好。

浏览器厂家在 CSS 表现力在表单方面的扩展做了一些尝试，在某些情况下，知道什么可用也挺不错的。

**警告：** 尽管 这些尝试很有趣，但**它们是非标准的，也就是不可靠的。**. 如果你使用它们 (也许你并不常用)，你要自己承担风险，使用非标准的属性对于 Web 并不是好事 。

* Mozilla CSS 扩展 (en-US)
    + `:-moz-placeholder` (en-US)
    + `:-moz-submit-invalid` (en-US)
    + `:-moz-ui-invalid` (en-US)
    + `:-moz-ui-valid`
* WebKit CSS 扩展
    + `::-webkit-input-placeholder` (en-US)
    + 其他
* Microsoft CSS 扩展
    + `:-ms-input-placeholder`
### 控制表单元素的外观

基于 WebKit(Chrome, Safari) 和 Gecko(Firefox) 的浏览器提供更高级的 HTML 部件定制。它们也实现了跨平台，因此需要一种方式把原生小部件转换为用户可设置样式的小部件。

为此，它们使用了专有属性：`-webkit-appearance` (en-US)或`-moz-appearance`。**这些属性是非标准的，不应该使用**。事实上，它们在 WebKit 和 Gecko 中的表现也是不相同的。然而，有一个值很好用：`none`，用这个值，你（几乎完全）能控制一个已知小部件的样式。

因此，如果你在应用一个元素的样式时遇到麻烦，可以尝试使用那些专有属性。我们下面有一些例子，这个属性最成功的例子是 WebKit 浏览器中的搜索域的样式：

```html
<form>
  <input type="search" />
</form>
```

```css
<style>
input[type=search] {
    border: 1px dotted #999;
    border-radius: 0;

    -webkit-appearance: none;
}
</style>
```

**备注：** 当我们谈及 Web 技术的时总是很难预测未来。扩展 CSS 表现力是很困难的，其他规范也做了一些探索性的工作，如Shadow DOM提供了一些观点。可完全设置样式的表单的问题还远未结束。

举例
--

### 复选框和单选按钮

独自设计复选框或单选按钮的样式是让人抓狂的。例如由于浏览器反应各不相同，在修改复选框和单选按钮的大小时，并不保证确实能改变它们。

#### 一个简单的测试用例

让我们研究一下下面的测试用例：

```html
<span><input type="checkbox" /></span>
```

```css
span {
  display: inline-block;
  background: red;
}

input[type="checkbox"] {
  width: 100px;
  height: 100px;
}
```

这里是不同的浏览器的处理方式：

| 浏览器 | 视图 |
| --- | --- |
| Firefox 57 (Mac OSX) |  |
| Firefox 57 (Windows 10) |  |
| Chrome 63 (Mac OSX) |  |
| Chrome 63 (Windows 10) |  |
| Internet Explorer 11 (Windows 10) |  |
| Edge 16 (Windows 10) |  |

#### 更复杂的例子

由于 Opera 和 Internet Explorer 没有像`-webkit-appearance` (en-US)或`-moz-appearance`这样的特性，使用它们是不合适的。幸运的是，CSS 有足够多的表现方式可以找到解决方法。让我们做一个很普通的例子：

```html
<form>
  <fieldset>
    <p>
      <input type="checkbox" id="first" name="fruit-1" value="cherry" />
      <label for="first">I like cherry</label>
    </p>
    <p>
      <input
 type="checkbox"
 id="second"
 name="fruit-2"
 value="banana"
 disabled />
      <label for="second">I can't like banana</label>
    </p>
    <p>
      <input type="checkbox" id="third" name="fruit-3" value="strawberry" />
      <label for="third">I like strawberry</label>
    </p>
  </fieldset>
</form>
```

带有一些基本的样式：

```css
body {
  font: 1em sans-serif;
}

form {
  display: inline-block;

  padding: 0;
  margin: 0;
}

fieldset {
  border: 1px solid #ccc;
  border-radius: 5px;
  margin: 0;
  padding: 1em;
}

label {
  cursor: pointer;
}

p {
  margin: 0;
}

p + p {
  margin: 0.5em 0 0;
}
```

**备注：** 下面的内容（仅限样式化 checkbox 部分）与英文版出入极大，猜测已经是过时内容

现在，让我们设计一个定制复选框的样式

计划用自己的图像替换原生的复选框，首先需要准备复选框在所有状态下的图像，那些状态是：未选、已选、禁用不选、禁用已选。该图像将用作 CSS 精灵：

![Check box CSS Sprite](/files/4173/checkbox-sprite.png)

一开始要隐藏初始复选框。可以简单的把它们从页面视图中拿开。这里要考虑两个重要的事情：

* 不能用`display:none`来隐藏复选框，因为后面我们需要把复选框对用户可见。而使用`display:none`，用户不能再访问这个复选框，这就表示复选框不能选择或不选择。
* 我们将使用 CSS3 选择器来实现定制的样式，为了支持旧版浏览器，可以在所有选择器前设置`:root`伪类。目前所有我们需要支持的浏览器都支持`:root`伪类，但是其他的并不能保证。这是一个过滤旧的 Internet Explorer 的便利方式的例子。那些旧版浏览器将看到传统的复选框，而新式的浏览器可以看到定制的复选框。

```css
:root input[type="checkbox"] {
  /\* original check box are push outside the viexport \*/
  position: absolute;
  left: -1000em;
}
```

现在加上自己的图像就可以摆脱原来的复选框了，为此，要在初始的复选框后面加上`<label>`元素，并使用它的`:before`伪元素。因此在下面章节中，要使用selector 属性来选择复选框，然后使用adjacent sibling selector来选择原有复选框后面的`label`。最后，访问`:before`伪元素来设计复选框显示定制样式。

```css
:root input[type="checkbox"] + label:before {
  content: "";
  display: inline-block;
  width: 16px;
  height: 16px;
  margin: 0 0.5em 0 0;
  background: url("https://developer.mozilla.org/files/4173/checkbox-sprite.png")
    no-repeat 0 0;

  /\* The following is used to adjust the position of
 the check boxes on the text baseline \*/

  vertical-align: bottom;
  position: relative;
  bottom: 2px;
}
```

在初始复选框上使用`:checked`和`:disabled`伪类来改变定制复选框的状态。因为使用了 CSS 精灵，我们需要做的只是修改背景的位置。

```css
:root input[type="checkbox"]:checked + label:before {
  background-position: 0 -16px;
}

:root input[type="checkbox"]:disabled + label:before {
  background-position: 0 -32px;
}

:root input[type="checkbox"]:checked:disabled + label:before {
  background-position: 0 -48px;
}
```

最后一件（但是很重要的）事情：当用户使用键盘从一个表单小部件导航到另一个表单小部件时，每个小部件都应该被显式聚焦。因为我们隐藏了初始的复选框，我们必须自己实现这个特性，让用户知道定制复选框在表单中的位置，下列的 CSS 实现了它们聚焦。

```css
:root input[type="checkbox"]:focus + label:before {
  outline: 1px dotted black;
}
```

你可以在线查看结果：

### Dealing with the select nightmare

`<select>` 元素被认为是一个 "丑陋的" 组件，因为不可能保证它在跨平台时样式化的一致性。然而，有些事情是可能的。废话少说，让我们来看一个例子：

```html
<select>
  <option>Cherry</option>
  <option>Banana</option>
  <option>Strawberry</option>
</select>
```

```css
select {
  width: 80px;
  padding: 10px;
}

option {
  padding: 5px;
  color: red;
}
```

下面的表格显示了在两种情况下不同浏览器的处理方式。头两列就是上面的例子。后面两列使用了其他的定制 CSS，可以对小部件的外观进行更多的控制：

```css
select,
option {
  -webkit-appearance: none; /\* To gain control over the appearance on WebKit/Chromium \*/
  -moz-appearance: none; /\* To gain control over the appearance on Gecko \*/

  /\* To gain control over the appearance on and Trident (IE)
 Note that it also works on Gecko and has partial effects on WebKit \*/
  background: none;
}
```

| Browser | Regular rendering | Tweaked rendering |
| --- | --- | --- |
| closed | open | closed | open |
| Firefox 57 (Mac OSX) |  |  |  |  |
| Firefox 57 (Windows 10) |  |  |  |  |
| Chrome 63 (Mac OSX) |  |  |  |  |
| Chrome 63 (Windows 10) |  |  |  |  |
| Opera 49 (Mac OSX) |  |  |  |  |
| IE11 (Windows 10) |  |  |  |  |
| Edge 16 (Windows 10) |  |  |  |  |

如你所见，计时使用了`-*-appearance`属性的帮助，任然有一些遗留的问题：

* 不同的操作系统和浏览器对属性`padding` 属性的处理各不相同。
* Internet Explorer 旧版本不允许平滑样式
* Firefox 没有实现下拉箭头的样式。
* 如果要在下拉列表内实现`<option>`元素样式，Chrome 和 Opera 浏览器的表现在不同的系统中是不一样的。

在我们的例子中，只使用了三个 CSS 属性，在考虑使用更多 CSS 属性时，可以想象是很混乱的。正如我们看到的，CSS 始终不适合用来修改这些小部件的外观，但是仍然可以用来稍微做一些事情。如果愿意的话，可以演示一下在不同操作系统和浏览器之间的区别。

我们也可以帮助了解在下一章节中哪个属性更合适：Properties compatibility table for form widgets (en-US)

走向更完美表单之路：有用的库和 polyfills（腻子）
-----------------------------

虽然对于复选框和单选按钮而言，CSS 的表示方式足够丰富，但是对更高级的小部件来说差距仍然很大。即使可以用`<select>`元素作一些事情，但是对 file 小部件的样式完全没用。对于日期选择器也同样如此。

要实现对表单小部件的完全控制，你别无选择，只能选择依靠 JavaScript。在文章How to build custom form widgets (en-US)中，我们将看到具体的做法，其中还有一些非常有用的库：

* Uni-form是一个对采用 CSS 样式的表单标记实现标准化的框架，在使用 jQuery 时，还提供一些附加特性，但这是可选的。
* Formalize是对公共 JavaScript 框架的扩展（如 jQuery, Dojo, YUI 等），有助于规范和定制表单。
* Niceforms是一个独立的 JavaScript 方法，能提供 web 表单的完整定制。

下面的库不止应用于表单，他们在处理 HTML 表单时是非常有趣的：

* jQuery UI做了一些有趣的改进，可以定制象日期选择器（特别关注无障碍）这样的小部件。
* Twitter Bootstrap在规范表单时是非常有用的。
* WebShim是一个大型工具，可以用来处理浏览器对 HTML5 的支持。对 web 表单部分确实有用。

记住，使用 CSS 和 JavaScript 是有副作用的。所以在选择使用那些库时，应该在脚本失败的情况下能回滚样式表。脚本失败的原因很多，尤其在手机应用中，因此你需要尽可能好的设计你的 Web 站点或应用。

总结
--

虽然 HTML 表单使用 CSS 仍有一些困难，但通常也有方法绕过它们。即使没有清楚的，通用的解决方案，但新式的浏览器也提供了新的可能性。目前最好的方法是更多的学习不同浏览器支持 CSS 的方式，并应用于 HTML 表单小部件。

在本指南的下一章节中，我们将探讨现代浏览器中用于为不同表单状态添加样式的可用的 UI 伪类。

* 上一页
* Overview: Forms
* 下一页
### 进阶内容

* 如何构建自定义表单控件
* 通过 JavaScript 发送表单
* 表单控件的属性兼容性列表