文档与网站架构 - 学习 Web 开发

文档与网站架构
=======

* 上一页
* Overview: Introduction to HTML
* 下一页

HTML 不仅能够定义网页的单独部分（例如“段落”或“图片”），还可以使用块级元素（例如“标题栏”、“导航菜单”、“主内容列”）来定义网站中的复合区域。本文将探讨如何规划基本的网站结构，并根据规划的结构来编写 HTML。

|  |  |
| --- | --- |
| 前提： |
 阅读
 开始学习 HTML、HTML 文字处理初步
 、创建超链接，掌握相关基础知识。
  |
| 学习目标： | 会用语义标签来构建文档，会搭建简单的网站结构。 |

文档的基本组成部分
---------

网页的外观多种多样，但是除了全屏视频或游戏，或艺术作品页面，或只是结构不当的页面以外，都倾向于使用类似的标准组件：

页眉

通常横跨于整个页面顶部有一个大标题 和/或 一个标志。这是网站的主要一般信息，通常存在于所有网页。

导航栏

指向网站各个主要区段的超链接。通常用菜单按钮、链接或标签页表示。类似于标题栏，导航栏通常应在所有网页之间保持一致，否则会让用户感到疑惑，甚至无所适从。许多 web 设计人员认为导航栏是标题栏的一部分，而不是独立的组件，但这并非绝对；还有人认为，两者独立可以提供更好的 无障碍访问特性，因为屏幕阅读器可以更清晰地分辨二者。

主内容

中心的大部分区域是当前网页大多数的独有内容，例如视频、文章、地图、新闻等。这些内容是网站的一部分，且会因页面而异。

侧边栏

一些外围信息、链接、引用、广告等。通常与主内容相关（例如一个新闻页面上，侧边栏可能包含作者信息或相关文章链接），还可能存在其他的重复元素，如辅助导航系统。

页脚

横跨页面底部的狭长区域。和标题一样，页脚是放置公共信息（比如版权声明或联系方式）的，一般使用较小字体，且通常为次要内容。还可以通过提供快速访问链接来进行 SEO。

一个“典型的网站”可能会这样布局：

![一个简单站点首页截图](/zh-CN/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure/sample-website.png)

用于构建内容的 HTML
------------

以上简单示例不是很精美，但是足够说明网站的典型布局方式了。一些站点拥有更多列，其中一些远比这复杂，但一切在你掌握之中。通过合适的 CSS，你可以使用相当多种的任意页面元素来环绕在不同的页面区域来做成你想要的样子，但如前所述，我们要敬畏语义，做到**正确选用元素**。

这是因为视觉效果并不是一切。我们可以修改最重要内容（例如导航菜单和相关链接）的颜色、字体大小来吸引用户的注意，但是这对视障人士是无效的，“粉红色”和“大字体”对他们并不奏效。

**备注：** 全球色盲患者比例为 4%，换句话说，每 12 名男性就有一位色盲，每 200 名女性就有一位色盲。全盲和视障人士约占世界人口（约 70 亿）的 13％（2015 年 全球约有 9.4 亿人口存在视力问题）。

HTML 代码中可根据功能来为区段添加标记。可使用元素来无歧义地表示上文所讲的内容区段，屏幕阅读器等辅助技术可以识别这些元素，并帮助执行“找到主导航“或”找到主内容“等任务。参见前文所讲的 页面中元素结构和语义不佳所带来的后果。

为了实现语义化标记，HTML 提供了明确这些区段的专用标签，例如：

* `<header>`：页眉。
* `<nav>`：导航栏。
* `<main>`：主内容。主内容中还可以有各种子内容区段，可用`<article>`、`<section>` 和 `<div>` 等元素表示。
* `<aside>`：侧边栏，经常嵌套在 `<main>` 中。
* `<footer>`：页脚。
### 主动学习：研究示例代码

上图的示例可用下面的代码表示（完整代码请参见 GitHub），请结合图片观察代码，并找出代码中可见的区段：

```html
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>二次元俱乐部</title>
    <link
 href="https://fonts.googleapis.com/css?family=Open+Sans+Condensed:300|Sonsie+One"
 rel="stylesheet" />
    <link
 href="https://fonts.googleapis.com/css?family=ZCOOL+KuaiLe"
 rel="stylesheet" />
    <link href="style.css" rel="stylesheet" />
  </head>

  <body>
    <header>
      <!-- 本站所有网页的统一主标题 -->
      <h1>聆听电子天籁之音</h1>
    </header>

    <nav>
      <!-- 本站统一的导航栏 -->
      <ul>
        <li><a href="#">主页</a></li>
        <!-- 共 n 个导航栏项目，省略…… -->
      </ul>

      <form>
        <!-- 搜索栏是站点内导航的一个非线性的方式。 -->
        <input type="search" name="q" placeholder="要搜索的内容" />
        <input type="submit" value="搜索" />
      </form>
    </nav>

    <main>
      <!-- 网页主体内容 -->
      <article>
        <!-- 此处包含一个 article（一篇文章），内容略…… -->
      </article>

      <aside>
        <!-- 侧边栏在主内容右侧 -->
        <h2>相关链接</h2>
        <ul>
          <li><a href="#">这是一个超链接</a></li>
          <!-- 侧边栏有 n 个超链接，略略略…… -->
        </ul>
      </aside>
    </main>

    <footer>
      <!-- 本站所有网页的统一页脚 -->
      <p>© 2050 某某保留所有权利</p>
    </footer>
  </body>
</html>
```

请花一些时间来阅读，其中的注释应该能够帮助你理解这些代码。现在能够理解上面的代码就可以，因为编写一套正确的 HTML 结构是理解文档布局的前提，布局工作就交给 CSS 吧。在学习 CSS 一章时我们再展开介绍。

HTML 布局元素细节
-----------

理解所有 HTML 区段元素具体含义是很有益处的，这一点将随着个人 web 开发经验的逐渐丰富日趋显现。更多细节请查阅 HTML 元素参考。现在，你只需要理解以下主要元素的意义：

* `<main>` 存放每个页面独有的内容。每个页面上只能用一次 `<main>`，且直接位于 `<body>` 中。最好不要把它嵌套进其他元素。
* `<article>` 包围的内容即一篇文章，与页面其他部分无关（比如一篇博文）。
* `<section>` 与 `<article>` 类似，但 `<section>` 更适用于组织页面使其按功能（比如迷你地图、一组文章标题和摘要）分块。一般的最佳用法是：以 标题 作为开头；也可以把一篇 `<article>` 分成若干部分并分别置于不同的 `<section>` 中，也可以把一个区段 `<section>` 分成若干部分并分别置于不同的 `<article>` 中，取决于上下文。
* `<aside>` 包含一些间接信息（术语条目、作者简介、相关链接，等等）。
* `<header>` 是简介形式的内容。如果它是 `<body>` 的子元素，那么就是网站的全局页眉。如果它是 `<article>` 或`<section>` 的子元素，那么它是这些部分特有的页眉（此 `<header>` 非彼 标题）。
* `<nav>` 包含页面主导航功能。其中不应包含二级链接等内容。
* `<footer>` 包含了页面的页脚部分。
### 无语义元素

有时你会发现，对于一些要组织的项目或要包装的内容，现有的语义元素均不能很好对应。有时候你可能只想将一组元素作为一个单独的实体来修饰来响应单一的用 CSS 或 JavaScript。为了应对这种情况，HTML 提供了 `<div>` 和 `<span>` 元素。应配合使用 `class` 属性提供一些标签，使这些元素能易于查询。

`<span>` 是一个内联的（inline）无语义元素，最好只用于无法找到更好的语义元素来包含内容时，或者不想增加特定的含义时。例如：

```html
<p>
  国王喝得酩酊大醉，在凌晨 1 点时才回到自己的房间，踉跄地走过门口。<span
 class="editor-note"
 >[编辑批注：此刻舞台灯光应变暗]</span
 >.
</p>
```

这里，“编辑批注”仅仅是对舞台剧导演提供额外指引；没有具体语义。对于视力正常的用户，CSS 应将批注内容与主内容稍微隔开一些。

`<div>` 是一个块级无语义元素，应仅用于找不到更好的块级元素时，或者不想增加特定的意义时。例如，一个电子商务网站页面上有一个一直显示的购物车组件。

```html
<div class="shopping-cart">
  <h2>购物车</h2>
  <ul>
    <li>
      <p>
        <a href=""><strong>银耳环</strong></a
 >：$99.95.
      </p>
      <img src="../products/3333-0985/" alt="Silver earrings" />
    </li>
    <li>...</li>
  </ul>
  <p>售价：$237.89</p>
</div>
```

这里不应使用 `<aside>`，因为它和主内容并没有必要的联系（你想在任何地方都能看到它）。甚至不能用 `<section>` ，因为它也不是页面上主内容的一部分。所以在这种情况下就应使用 `<div>`，为满足无障碍使用特征，还应为购物车的标题设置一个可读标签。

**警告：** div 元素非常便利但容易被滥用。由于它们没有语义值，会使 HTML 代码变得混乱。要小心使用，只有在没有更好的语义方案时才选择它，而且要尽可能少用，否则文档的升级和维护工作会非常困难。

### 换行与水平分割线

有时会用到 `<br>` 和 `<hr>` 两个元素，需要介绍一下。

####
：换行元素

`<br>` 可在段落中进行换行；`<br>` 是唯一能够生成多个短行结构（例如邮寄地址或诗歌）的元素。比如：

```html
<p>
  从前有个人叫小高<br />
  他说写 HTML 感觉最好<br />
  但他写的代码结构语义一团糟<br />
  他写的标签谁也懂不了。
</p>
```

没有 `<br>` 元素，这段会直接显示在长长的一行中（如前文所讲，HTML 会忽略大部分空格）；使用 `<br>` 元素，才使得诗看上去像诗：

####
：主题性中断元素

`<hr>` 元素在文档中生成一条水平分割线，表示文本中主题的变化（例如话题或场景的改变）。一般就是一条水平的直线。例如：

```html
<p>
  原来这唐僧是个慈悯的圣僧。他见行者哀告，却也回心转意道：“既如此说，且饶你这一次。再休无礼。如若仍前作恶，这咒语颠倒就念二十遍！”行者道：“三十遍也由你，只是我不打人了。”却才伏侍唐僧上马，又将摘来桃子奉上。唐僧在马上也吃了几个，权且充饥。
</p>
<hr />
<p>
  却说那妖精，脱命升空。原来行者那一棒不曾打杀妖精，妖精出神去了。他在那云端里，咬牙切齿，暗恨行者道：“几年只闻得讲他手段，今日果然话不虚传。那唐僧已此不认得我，将要吃饭。若低头闻一闻儿，我就一把捞住，却不是我的人了。不期被他走来，弄破我这勾当，又几乎被他打了一棒。若饶了这个和尚，诚然是劳而无功也。我还下去戏他一戏。”
</p>
```

将渲染成：

规划一个简单的网站
---------

在完成页面内容的规划后，一般应按部就班地规划整个网站的内容，要可能带给用户最好的体验，需要哪些页面、如何排列组合这些页面、如何互相链接等问题不可忽略。这些工作称为信息架构。在大型网站中，大多数规划工作都可以归结于此，而对于一个只有几个页面的简单网站，规划设计过程可以更简单，更有趣！

1. 时刻记住，大多数（不是全部）页面会使用一些相同的元素，例如导航菜单以及页脚内容。若网站为商业站点，不妨在所有页面的页脚都加上联系方式。请记录这些对所有页面都通用的内容：
 ![所有页面共有的内容，包括：站点标题、Logo、联系方式、版权声明、语言等信息。](/zh-CN/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure/common-features.png)
2. 接下来，可为页面结构绘制草图（这里与前文那个站点页面的截图类似）。记录每一块的作用：
 ![简单的页面布局示意图，有页眉、页脚、主内容、侧边栏。](/zh-CN/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure/site-structure.png)
3. 下面，对于期望添加进站点的所有其他（通用内容以外的）内容展开头脑风暴，直接罗列出来。
 ![把假日旅行站点的所有功能罗列到一个列表中](/zh-CN/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure/feature-list.png)
4. 下一步，试着对这些内容进行分组，这样可以让你了解哪些内容可以放在同一个页面。这种做法和 卡片分类法 非常相似。
 ![假日网站的页面应分 5 类：搜索、特别提供、具体国家信息、搜索结果、购物。](/zh-CN/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure/card-sorting.png)
5. 接下来，试着绘制一个站点地图的草图，使用一个气泡代表网站的一个页面，并绘制连线来表示页面间的一般工作流。主页面一般置于中心，且链接到其他大多数页面；小型网站的大多数页面都可以从主页的导航栏中链接跳转。也可记录下内容的显示方式。
 ![](/zh-CN/docs/Learn/HTML/Introduction_to_HTML/Document_and_website_structure/site-map.png)
### 主动学习：创建站点地图

自己创造一个网站（什么网站呢？）并尝试执行上述步骤。

**备注：** 记得保存你的杰作，今后可能会用到哦。

小结
--

现在你应该对如何构建网页/站点有了更好的理解。下一节我们将学习如何调试 HTML。

参见
--

* 使用 HTML 区段和大纲：HTML 语义元素和大纲算法进阶指南。
* 上一页
* Overview: Introduction to HTML
* 下一页


作业：构建行星数据 - 学习 Web 开发

作业：构建行星数据
=========

* 上一页
* Overview: Tables

在我们的表格评定中，我们为你提供有关太阳系中行星的一些数据，并让你将其结构化成 HTML 表。

|  |  |
| --- | --- |
| 前提： | 在尝试这个评定之前，你应该已经把这个模块的所有文章都学习完成了。 |
| 目标： | 检测对 HTML 表格及相关功能的理解。 |

起点
--

要进行本测验，将 blank-template.html, minimal-table.css 和 planets-data.txt 拷贝到本地。

**备注：** 另外，你可以使用 JSBin 或 Glitch 来做你的测验。你可以把 HTML, CSS 和 JavaScript 粘贴到其中一个网上编辑器里。如果你使用的网上编辑器不支持 JavaScript/CSS 文件链接到 HTML 中使用，那么也可以使用 `<script>`/`<style>` 元素将它们直接写在你的 HTML 页面里。

项目概要
----

你在学校工作; 目前，你的学生正在学习太阳系的行星，然后你想为他们提供一份简单的易于追踪的数据集合，来查找有关行星的数字和情况。一张 HTML 数据表将是理想的，你需要先获得可用的数据，然后把它变成一张表格，跟着下面的步骤。

完成后的表格看上去应该是这样的：

![](/zh-CN/docs/Learn/HTML/Tables/Structuring_planet_data/assessment-table.png)

你也可以 在线查看 (不要偷看源代码，不要作弊哦 !)

要完成的步骤
------

下面的步骤描述了：为了完成这个示例的表格，你所要做的事情。所有你需要的数据都包含在`planets-data.txt` 文件中。如果你在获得这些数据时遇到了问题，也看看上面的实例，或者尝试绘制一个图。

1. 打开在你本地环境中的 `blank-template.html`副本，提供一个外部容器来初始化表格，一个表格 header，一个表格 body。在这个例子中，你不需要表格 footer。
2. 为你的表格添加我们提供的标题。
3. 在表格的 header 中添加一行，用来包括所有列的标题。
4. 在表格的 body 部分创建所有内容行，记住要让所有是行标题的单元格语义化。
5. 确保所有内容都插入了正确的单元格，在原始数据中，每行行星数据都显示在其相关行星的旁边。
6. 添加一些属性，让行标题和列标题更加明确地与和它们有关的单元格进行关联，使用 rowgroups 让子标题和父标题也进行关联。
7. 为包含所有行星标题的行标题的那一列数据，添加一个黑色边框
要点和提示
-----

* 标题行的第一个单元格需要是空白的，然后宽度为 2 个单元格。
* 行的主标题 (e.g. *Jovian planets*) 以及放置在行星名称行左侧的标题 (e.g. *Saturn*) 整理出来有点麻烦， — 你需要确保每个单元格都有正确的高度和宽度。(即横跨正确的行数和列数)
* 将标题与其行/列相关联的一种方法比其他方法容易得多。
评定
--

如果你将此评估作为有组织的课程的一部分，你应该能够将你的工作交给你的老师/导师进行评改。如果你是自学习的，那么你可以通过询问 Learning Area Discourse thread, 或在 #mdn的 IRC 频道 Mozilla IRC 中轻松获得标记指南。首先尝试练习 - 作弊对你没有益处！

* 上一页
* Overview: Tables


HTML 表格高级特性和无障碍 - 学习 Web 开发

HTML 表格高级特性和无障碍
===============

* 上一页
* Overview: Tables
* 下一页

这个模块的第二篇文章中，我们来看一下 HTML 表格更高级的功能，比如表格的标题、摘要，以及将你表格中的各行分组成表头、表体、表尾三部分，以及为视力受损的用户提高表格的无障碍性。

|  |  |
| --- | --- |
| 前提： |
 HTML 的基础知识（参见
 HTML 简介）。
  |
| 目标： | 学习 HTML 表格进一步的功能，以及表格的无障碍访问性。 |

使用 <caption> 为你的表格增加一个标题
------------------------

你可以通过 `<caption>` 元素为你的表格增加一个标题，再把 `<caption>` 元素放入 `<table>` 元素中。你应该把它放在 `<table>` 开始标签的下面。

```html
<table>
  <caption>
    侏罗纪时期的恐龙
  </caption>

  …
</table>
```

从上面简单的例子可以推断，标题意味着包含对于表格内容的描述，这对那些希望可以快速浏览网页中的表格对他们是否有帮助的读者们来说，是非常好的功能。特别是盲人用户，不需要让屏幕阅读设备读出很多单元格的内容，来让用户了解这张表格讲的是什么，而是可以依靠标题的内容，来决定是否需要了解更详细的内容。

标题就放在 `<table>` 标签的下面。

**备注：** `summary` 属性也可以在 `<table>` 元素中使用，用来提供一段描述，同样可以被屏幕阅读设备阅读。我们推荐使用 `<caption>` 元素来代替，因为 `summary` 已经废弃了，且不能被视力正常的用户阅读（它不会出现在页面上）。

### 动手练习：添加一个标题

我们来试试看吧，回顾一下我们在之前的文章中第一次遇到的例子。.

1. 打开你的语言老师的学校时间表，就是 HTML 表格基础 结尾中的例子，或者把 timetable-fixed.html 文件复制到本地。
2. 为表格添加一个合适的标题。
3. 保存你的代码，然后用浏览器打开，看看你的表格是什么样的。

**备注：** 你也可以在 GitHub 上找到我们的版本 timetable-caption.html（查看在线运行版本）。

添加 <thead>、<tfoot> 和 <tbody> 结构
-------------------------------

由于你的表格在结构上有点复杂，如果把它们定义得更加结构化，那会帮助我们更能了解结构。一个明确的方法是使用 `<thead>`、`<tfoot>` 和 `<tbody>`，这些元素允许你把表格中的部分标记为表头、表尾、表体三部分。

这些元素不会使表格更易于屏幕阅读器用户访问，也不会造成任何视觉上的改变。然而，它们在应用样式和布局上会起到作用，可以更好地让 CSS 应用到表格上。给你一些有趣的例子，在长表格的情况下，你可以在每个打印页面上使表格页眉和页脚重复，你也可以让表格的正文部分显示在一个单独的页面上，并通过上下滚动来获得内容。

试着使用它们：

* `<thead>` 元素必须包住表格中作为表头的部分。一般是第一行，往往都是每列的标题，但是不是每种情况都是这样的。如果你使用了 `<col>`/`<colgroup>` 元素，那么 `<thead>` 元素就需要放在它们的下面。
* `<tfoot>` 元素需要包住表格中作为表脚的部分。一般是最后一行，往往是对前面所有行的总结，比如，你可以按照预想的方式将`<tfoot>`放在表格的底部，或者就放在 `<thead>` 的下面。(浏览器仍将它呈现在表格的底部)
* `<tbody>` 元素需要包住表格内容中不在表头或表尾的其他部分。它可以出现在表头的下方，或者有时出现在表脚下方，这取决于你如何安排它。

**备注：** `<tbody>` 总是包含在每个表中，如果你没有在代码中指定它，那就是隐式的。可以来验证一下，打开一个你之前没有包含 `<tbody>` 的例子，然后在你的浏览器开发者工具中观察你的代码，你会看到浏览器为你添加了这个标签。你也许会想问，为什么你应该在所有表中都需要这个元素，因为它可以让你更好地控制表格结构和样式。

### 动手练习：添加表格结构

让我们动手使用这些新元素。

1. 首先，把 spending-record.html 和 minimal-table.css 复制到你的本地环境。
2. 尝试在浏览器中打开它，你会发现看起来不错，但是它可以被改善得更好。“SUM”行包含了已经使用的金额的总和，不过它出现在了错误的位置，以及代码中还遗失了一些细节。
3. 将明显的标题行改为使用 `<thead>` 元素，“SUM”行使用 `<tfoot>` 元素，剩余的内容使用 `<tbody>` 元素。
4. 先保存，再刷新。你会看到，添加了 `<tfoot>` 元素后，“SUM”行跑到了表格的底部。
5. 接着，添加一个 `colspan` (en-US) 属性，使“SUM”单元格占 4 个单元格的位置，所以实际数字是显示在“Cost”列的底部。
6. 让我们为表格添加一些简单的额外属性，能够让你理解这些属性是如何帮助更好地让表格应用 CSS 的。在你的 HTML 文件的 head 标签部分，你会看到一个空的 `<style>` 元素。在 style 元素中添加下列 CSS 代码：

```css
tbody {
  font-size: 95%;
  font-style: italic;
}
tfoot {
  font-weight: bold;
}
```

7. 先保存，再刷新，然后观察一下结果。如果没有 `<tbody>` 和 `<tfoot>` 元素，你也许会写更加复杂的选择器或规则来应用同样的样式。

**备注：** 我们并不期望你现在就能完全理解 CSS。在你阅读我们的 CSS 模块时，你会学到更多这方面的知识（CSS 介绍是一个好的起点；文章样式化表格也专门讲述了如何对表格添加样式）。

你完成的表格应该如下所示：
```

<!doctype html>
<html lang="en-US">
  <head>
    <meta charset="utf-8" />
    <title>My spending record</title>
    <style>
 html {
 font-family: sans-serif;
 }

 table {
 border-collapse: collapse;
 border: 4px solid rgb(200, 200, 200);
 letter-spacing: 1px;
 font-size: 0.8rem;
 }

 td,
 th {
 border: 2px solid rgb(190, 190, 190);
 padding: 10px 20px;
 }

 th {
 background-color: rgb(235, 235, 235);
 }

 td {
 text-align: center;
 }

 tr:nth-child(even) td {
 background-color: rgb(250, 250, 250);
 }

 tr:nth-child(odd) td {
 background-color: rgb(245, 245, 245);
 }

 caption {
 padding: 10px;
 }

 tbody {
 font-size: 90%;
 font-style: italic;
 }

 tfoot {
 font-weight: bold;
 }
 </style>
  </head>
  <body>
    <table>
      <caption>
        How I chose to spend my money
      </caption>
      <thead>
        <tr>
          <th>Purchase</th>
          <th>Location</th>
          <th>Date</th>
          <th>Evaluation</th>
          <th>Cost (€)</th>
        </tr>
      </thead>
      <tfoot>
        <tr>
          <td colspan="4">SUM</td>
          <td>118</td>
        </tr>
      </tfoot>
      <tbody>
        <tr>
          <td>Haircut</td>
          <td>Hairdresser</td>
          <td>12/09</td>
          <td>Great idea</td>
          <td>30</td>
        </tr>
        <tr>
          <td>Lasagna</td>
          <td>Restaurant</td>
          <td>12/09</td>
          <td>Regrets</td>
          <td>18</td>
        </tr>
        <tr>
          <td>Shoes</td>
          <td>Shoeshop</td>
          <td>13/09</td>
          <td>Big regrets</td>
          <td>65</td>
        </tr>
        <tr>
          <td>Toothpaste</td>
          <td>Supermarket</td>
          <td>13/09</td>
          <td>Good</td>
          <td>5</td>
        </tr>
      </tbody>
    </table>
  </body>
</html>
```

**备注：** 你也可以在 GitHub 上找到 spending-record-finished.html（查看在线运行版本）。

嵌套表格
----

在一个表格中嵌套另外一个表格是可能的，只要你包含完整的结构，包括 `<table>` 元素。这样通常是不建议的，因为这种做法会使标记看上去很难理解，对使用屏幕阅读的用户来说，无障碍性也降低了。以及在很多情况下，也许你只需要插入额外的单元格、行、列到已有的表格中。然而有时候是必要的，比如你想要从其他资源中更简单地导入内容。

下面的代码演示了一个简单的嵌套表格：

```html
<table id="table1">
  <tr>
    <th>标题 1</th>
    <th>标题 2</th>
    <th>标题 3</th>
  </tr>
  <tr>
    <td id="nested">
      <table id="table2">
        <tr>
          <td>单元格 1</td>
          <td>单元格 2</td>
          <td>单元格 3</td>
        </tr>
      </table>
    </td>
    <td>单元格 2</td>
    <td>单元格 3</td>
  </tr>
  <tr>
    <td>单元格 4</td>
    <td>单元格 5</td>
    <td>单元格 6</td>
  </tr>
</table>
```

输出看起来是这样的：
```

table {
  border-collapse: collapse;
}
td,
th {
  border: 1px solid black;
  padding: 10px 20px;
}
```

对于视力受损的用户的表格
------------

让我们简要回顾一下如何使用数据表。一个表格可以是一个便利的工具，或者让我们快速访问数据，并允许我们查找不同的值。比如，你只需要稍微看一眼下列的表格，你就能得知 2016 年 8 月份在 Gent 出售了多少个戒指。为了理解信息，我们让数据与列标题或行标题之间建立视觉联系。

2016 年 8 月出售的物品|  |  | 衣物 | 饰品 |
|  |  | 长裤 | 衬衫 | 裙子 | 手镯 | 戒指 |
| 比利时 | Antwerp | 56 | 22 | 43 | 72 | 23 |
| Gent | 46 | 18 | 50 | 61 | 15 |
| Brussels | 51 | 27 | 38 | 69 | 28 |
| 荷兰 | Amsterdam | 89 | 34 | 69 | 85 | 38 |
| Utrecht | 80 | 12 | 43 | 36 | 19 |

但假设你无法通过视觉关联这些数据呢？那么你应该如何阅读上述的表格？视力受损的用户经常使用一个屏幕阅读设备来为他们读出网页上的信息。对于盲人来说，阅读简单的文字没有什么问题，但是要理解一张表格的内容，这就有一些难度了。然而，通过适当的标记，我们可以用程序化的关联来取代视觉关联。

**备注：** 根据世界卫生组织 2017 年的数据，大约有 2.53 亿人患有视觉障碍。

本篇文章提供了更一步的技术来使表格的无障碍性尽可能地提高。

### 使用列和行的标题

屏幕阅读设备会识别所有的标题，然后在它们和它们所关联的单元格之间产生程序关联。列和行标题的组合将标识和解释每个单元格中的数据，以便屏幕阅读器用户可以类似于视力正常的用户的操作来理解表格。

我们之前的文章就提到过这一点，可见使用 <th> 元素添加标题。

### scope 属性

本篇文章的一个新话题是 `scope` 属性，可以添加在 `<th>` 元素中，以告诉屏幕阅读器该表头的类型——它是所在行的表头，还是所在列的表头。回想一下我们前面的消费记录例子，你可以像这样明确地把表头定义为所在列的头部：

```html
<thead>
  <tr>
    <th scope="col">Purchase</th>
    <th scope="col">Location</th>
    <th scope="col">Date</th>
    <th scope="col">Evaluation</th>
    <th scope="col">Cost (€)</th>
  </tr>
</thead>
```

而每一行都可以有一个像这样定义的标题（如果我们把行的标题和列的标题都加上）：

```html
<tr>
  <th scope="row">Haircut</th>
  <td>Hairdresser</td>
  <td>12/09</td>
  <td>Great idea</td>
  <td>30</td>
</tr>
```

屏幕阅读器会识别这种结构化的标记，并一次读出整列或整行。

`scope` 还有两个可选的值： `colgroup` 和 `rowgroup`。这些用于位于多个列或行的顶部的标题。如果你回顾这部分文章开始部分的“2016 年 8 月出售的物品”表格，你会看到“衣物”单元格在“长裤”、“衬衫”和“裙子”单元格的上面。这几个单元格都应该被标记为表头（`<th>`），但是“衣物”是一个位于顶部且定义了其他三个子标题的表头。因此“衣物”应该有一个 `scope="colgroup"` 属性，而另外三个子标题应该有 `scope="col"` 属性。

### id 和标题属性

如果要替代 `scope` 属性，可以使用 `id` 和 `headers` (en-US) 属性来创建标题与单元格之间的联系。使用方法如下：

1. 为每个 `<th>` 元素添加一个唯一的 `id` 。
2. 为每个 `<td>` 元素添加一个 `headers` 属性。每个单元格的 `headers` 属性需要包含它从属于的所有标题的 id，之间用空格分隔开。

这会给你的 HTML 表格中每个单元格的位置一个明确的定义。像一个电子表格一样，通过 headers 属性来定义属于哪些行或列。为了让它工作良好，表格同时需要列和行标题。

回到我们的花费成本示例，前两个片段可以重写为：

```html
<thead>
  <tr>
    <th id="purchase">Purchase</th>
    <th id="location">Location</th>
    <th id="date">Date</th>
    <th id="evaluation">Evaluation</th>
    <th id="cost">Cost (€)</th>
  </tr>
</thead>
<tbody>
  <tr>
    <th id="haircut">Haircut</th>
    <td headers="location haircut">Hairdresser</td>
    <td headers="date haircut">12/09</td>
    <td headers="evaluation haircut">Great idea</td>
    <td headers="cost haircut">30</td>
  </tr>

  …
</tbody>
```

**备注：** 这种方法在标题和数据单元格之间建立了非常精确的关联，但是它使用了**非常多**的标记，而且没有留下任何错误的空间。对于大多数表格来说，`scope` 方法通常就足够了。

### 动手练习：使用 scope 和 headers

1. 对于这个最后的练习，首先把 items-sold.html 和 minimal-table.css 复制到你的本地环境。
2. 现在尝试添加适当的 `scope` 属性来让表格变得更加恰当。
3. 最后，尝试把未添加 `scope` 属性的源文件再复制一份。这次使用 `id` 和 `headers` 属性让表格变得更加恰当。

**备注：** 你可以根据我们完成的例子检查你的工作，请看 items-sold-scope.html (also see this live) 和 items-sold-headers.html（查看在线运行版本）。

总结
--

关于 HTML 表格你还可以学习其他一些东西，但是我们目前已经把大部分你需要知道的内容都告诉你了。接下来，你可以在 HTML 表格测评中测试自己的水平了，祝你玩得开心！

如果你已经在学习 CSS，并且在评估中表现良好，你就可以继续学习 HTML 表格的样式，参见样式化表格。

如果你想要入门 CSS，请前往 CSS 学习区！

* 上一页
* Overview: Tables
* 下一页


HTML 表格基础 - 学习 Web 开发

HTML 表格基础
=========

* Overview: Tables
* 下一页

本文将从 HTML 表格开始，介绍一些基本的内容，如行和单元格、标题、使单元格跨越多个列和行，以及如何将列中的所有单元组合在一起进行样式化。

|  |  |
| --- | --- |
| 前置知识： | HTML 基本概念（参见HTML 介绍）。 |
| 目标： | 了解熟悉 HTML 表格基本知识。 |

什么是表格？
------

表格是由行和列组成的结构化数据集（表格数据），它让你快速简单地查找某个表示不同类型数据之间的某种关系的值。比如说，某个人和他的年龄，一天或是一周，当地游泳池的时间表。

![A sample table showing names and ages of some people - Chris 38, Dennis 45, Sarah 29, Karen 47.](/zh-CN/docs/Learn/HTML/Tables/Basics/numbers-table.png)

![A swimming timetable showing a sample data table](/zh-CN/docs/Learn/HTML/Tables/Basics/swimming-timetable.png)

表格在人类社会中很常见，而且已经存在很长时间了，下面这张 1800 年的美国人口普查文件中就可以证明：

![A very old parchment document; the data is not easily readable, but it clearly shows a data table being used.](/zh-CN/docs/Learn/HTML/Tables/Basics/1800-census.jpg)

因此，HTML 的创建者们提供了一种方法来构建和呈现 web 上的表格数据，这也就不足为奇了。

### 表格如何工作？

表格的一个特点就是严格。通过在行和列的标题之间进行视觉关联的方法，就可以让信息能够很简单地被解读出来。观察下面的示例表格，然后找一个单数人称代词，这个单数人称代词是用于第三人称的，用于女性的，用作句子中的对象。你可以把相应的行和列的标题关联起来，找到答案。

人称代词

|  | Subject | Object |
| --- | --- | --- |
| 单数 | 第一人称 | I | me |
| 第二人称 | you | you |
| 第三人称 | ♂ | he | him |
| ♀ | she | her |
| o | it | it |
| 复数 | 第一人称 | we | us |
| 第二人称 | you | you |
| 第三人称 | they | them |

正确完成后，即使是盲人也可以解析 HTML 表格中的数据，一个成功的 HTML 表格应该做到无论用户是视力正常还是视力受损，都能提高用户的体验。

### 表格风格

你可以在 GitHub 上找到上面表格的 HTML 源码；先去看看，当然也可以看看这个在线示例！你也许会注意到一件事情，那就是这个表格看上去可读性不是很好，那是因为现在这个页面上面的那个表格通过 MDN 站点添加了一些样式，而 GitHub 上面的并没有添加。

不要幻想；为了能够让表格在网页上有效，你需要提供一些 CSS 的样式信息，以及尽可能好的 HTML 固定结构。在这个模块中，我们将专注于 HTML 部分；在你完成这里的内容之后，你可以浏览 样式化表格 来了解 CSS 的部分。

虽然在这个模块中我们不会专注于 CSS，但是我们提供了一个较小的 CSS 样式表让你使用，和默认的没有采用任何 CSS 样式的表相比，表格会更加可读。你可以在这里获取样式表，以及在 HTML template 获取 HTML 文件来应用样式表，这些会让你在“测试 HTML 表格”中有一个好的起点。

**备注：** 也可以看下 personal\_pronouns table with this styling applied 这个版本，这个是应用了 CSS 以后表格看上去的样子。

### 什么时候你不应该使用 HTML 表格？

HTML 表格应该用于表格数据，这正是 HTML 表格设计出来的用途。不幸的是，许多人习惯用 HTML 表格来实现网页布局，例如：一行包含头部，一行包含几列内容，一行包含尾部。你可以在我们的无障碍学习模块中的页面布局章节获得更多细节内容和一个示例。这种做法以前是很常见的，因为以前 CSS 在不同浏览器上的兼容性比较糟糕；表格布局现在不太普遍，但你可能仍然会在网络的某些角落看到它们。

简单来说，使用表格布局而不使用 CSS 布局技巧 是很糟糕的。主要的理由有以下几个：

1. **表格布局减少了视觉受损的用户的无障碍**: 盲人所使用的屏幕阅读器会解析存在于 HTML 页面上的标签，然后为用户读出其中的内容。因为对于布局来说，表格不是一个正确的工具，使用的标记比使用 CSS 布局技术更复杂，所以屏幕阅读器的输出会让他们的用户感到困惑。
2. **表格会产生很多标签**: 正如刚才提到的，表格布局通常会比正确的布局技术涉及更复杂的标签结构，这会导致代码变得更难于编写、维护、调试。
3. **表格不能自动响应**: 当你使用正确的布局容器（比如 `<header>`、`<section>`、`<article>` 或是 `<div>`）时，它们的默认宽度是父元素的 100%；而表格的的默认大小是根据其内容而定的。因此，需要采取额外的措施来获取表格布局样式，以便有效地在各种设备上工作。
动手练习：创建你的第一个表格
--------------

对于表格的理论知识，我们已经说了很多了，所以，让我们来看一个使用的例子，并建立一个简单的表格。

1. 首先，将 blank-template.html 和 minimal-table.css 文件拷贝到你的本地环境上。
2. 每一个表格的内容都包含在这两个标签中：**`<table></table>`**。在你的 HTML 的 `<body>` 中添加这些内容。
3. 在表格中，最小的内容容器是单元格，是通过 **`<td>` (en-US)** 元素创建的（其中“td”代表“table data”）。把下面的内容添加到你的表格标签中：

```html
<td>Hi, I'm your first cell.</td>
```

4. 如果我们想要一行四个单元格，我们需要把这组标签拷贝三次，更新你表中的内容，让它看起来是这样的：

```html
<td>Hi, I'm your first cell.</td>
<td>I'm your second cell.</td>
<td>I'm your third cell.</td>
<td>I'm your fourth cell.</td>
```

你会看到，单元格不会放置在彼此的下方，而是自动与同一行上的其他单元格对齐。每个 `<td>` 元素 创建一个单独单元格，它们共同组成了第一行。我们添加的每个单元格都使行的长度变长。

如果想让这一行停止增加，并让单元格从第二行开始，我们需要使用 **`<tr>`** 元素（其中“tr”代表“table row”）。让我们现在来证实一下。

1. 把你已经创建好的 4 个单元格放入 `<tr>` 标签，就像这样：

```html
<tr>
  <td>Hi, I'm your first cell.</td>
  <td>I'm your second cell.</td>
  <td>I'm your third cell.</td>
  <td>I'm your fourth cell.</td>
</tr>
```

2. 现在你已经实现了一行，可以继续增加至两行、三行。每一行都需要一个额外的 `<tr>` 元素来包装，每个单元格的内容都应该写在 `<td>`中。
### 结果

这样会产生一个如下所示的表：
```

<table>
  <tr>
    <td>Hi, I'm your first cell.</td>
    <td>I'm your second cell.</td>
    <td>I'm your third cell.</td>
    <td>I'm your fourth cell.</td>
  </tr>

  <tr>
    <td>Second row, first cell.</td>
    <td>Cell 2.</td>
    <td>Cell 3.</td>
    <td>Cell 4.</td>
  </tr>
</table>
```

```
table {
  border-collapse: collapse;
}
td,
th {
  border: 1px solid black;
  padding: 10px 20px;
}
```

**备注：** 你也可以在 GitHub 中查看 simple-table.html (也可以查看在线示例).

使用 <th> 元素添加标题
--------------

现在，让我们把注意力转向表格标题，表格中的标题是特殊的单元格，通常在行或列的开始处，定义行或列包含的数据类型（举个例子，看到本篇文章中第一个示例中的 "单数" 或者 "Object"）。为了说明它们为什么这么有用，来看下面这个例子，首先是源代码：

```html
<table>
  <tr>
    <td> </td>
    <td>Knocky</td>
    <td>Flor</td>
    <td>Ella</td>
    <td>Juan</td>
  </tr>
  <tr>
    <td>Breed</td>
    <td>Jack Russell</td>
    <td>Poodle</td>
    <td>Streetdog</td>
    <td>Cocker Spaniel</td>
  </tr>
  <tr>
    <td>Age</td>
    <td>16</td>
    <td>9</td>
    <td>10</td>
    <td>5</td>
  </tr>
  <tr>
    <td>Owner</td>
    <td>Mother-in-law</td>
    <td>Me</td>
    <td>Me</td>
    <td>Sister-in-law</td>
  </tr>
  <tr>
    <td>Eating Habits</td>
    <td>Eats everyone's leftovers</td>
    <td>Nibbles at food</td>
    <td>Hearty eater</td>
    <td>Will eat till he explodes</td>
  </tr>
</table>
```

这是表格实际呈现的效果：

这里的问题是：虽然你可以弄清楚发生了什么，但是尽可能的交叉参考数据并不容易。如果列和行的标题以某种方式出现，那将会更好。

### 动手练习：表格标题

让我们来改进这个表格。

1. 首先，把 dogs-table.html 和 minimal-table.css 文件保存到你的本地环境，HTML 文件包含上文你看到的几种狗的数据。
2. 为了将表格的标题在视觉上和语义上都能被识别为标题，你可以使用 **`<th>`** 元素（其中 'th' 代表 'table header'），用法和 `<td>`是一样的，除了它表示为标题，不是普通的单元格以外。进入你的 HTML 文件，将表格中应该是标题的 `<td>` 元素标记的内容，都改为用 `<th>` 元素标记。
3. 保存你的 HTML 文件，然后在浏览器中加载，然后你应该会看到，现在的标题更像标题了。

**备注：** 你可以在 GitHub 中找到完成的版本 dogs-table-fixed.html (也可以查看在线示例)。

### 为什么标题是有用的？

我们已经给出了部分答案，当标题明显突出的时候，你可以更加简单地找到你想找的数据，设计上也会看起来更好。

**备注：** 即使你不给表格添加你自己的样式，表格标题也会带有一些默认样式：加粗和居中，让标题可以突出显示。

表格标题也有额外的好处，随着 `scope` 属性 (我们将在下一篇文章中了解到)，这个属性允许你让表格变得更加无障碍，每个标题与相同行或列中的所有数据相关联。屏幕阅读设备能一次读出一列或一行的数据，这是非常有帮助的。

允许单元格跨越多行和列
-----------

有时我们希望单元格跨越多行或多列。以下是一个简单的例子，显示了一些常见动物的名字。在某些情况下，我们要显示动物名称旁边的男性和女性的名字。有时候我们又不需要，那不需要的情况下，我们希望写着动物的名字的单元格的宽度可以是两个单元格的宽度 (因为写着名字的行会有两列，而没有写名字的行只有一列，行的宽度是不一样的)。

一开始的标记写法是这样的：

```html
<table>
  <tr>
    <th>Animals</th>
  </tr>
  <tr>
    <th>Hippopotamus</th>
  </tr>
  <tr>
    <th>Horse</th>
    <td>Mare</td>
  </tr>
  <tr>
    <td>Stallion</td>
  </tr>
  <tr>
    <th>Crocodile</th>
  </tr>
  <tr>
    <th>Chicken</th>
    <td>Hen</td>
  </tr>
  <tr>
    <td>Rooster</td>
  </tr>
</table>
```

```
table {
  border-collapse: collapse;
}
td,
th {
  border: 1px solid black;
  padding: 10px 20px;
}
```

但是输出的结果不是我们想要的：

我们需要一个方法，让 "Animals"、"Hippopotamus" 和 "Crocodile" 的单元格的宽度变为两个单元格， "Horse" 和 "Chicken" 的高度变为两行 (因为要拥有一个男性名字和女性名字，可以先看效果图)。幸好，表格中的标题和单元格有 `colspan` 和 `rowspan` 属性，这两个属性可以帮助我们实现这些效果。这两个属性接受一个没有单位的数字值，数字决定了它们的宽度或高度是几个单元格。比如，`colspan="2"` 使一个单元格的宽度是两个单元格。

让我们使用 `colspan` 和 `rowspan` 来改进现有的表格。

1. 首先，把 animals-table.html 和 minimal-table.css 文件复制到你的本地环境，HTML 文件中包含了你刚才看到的动物示例的数据。
2. 接着，使用 `colspan` 让 "Animals"、"Hippopotamus" 和 "Crocodile" 占 2 个单元格的宽度。
3. 最后，使用 `rowspan` 让 "Horse" 和 "Chicken" 占 2 个单元格的高度。
4. 保存后，用浏览器打开你写的 HTML 文件，看看改进的地方。

**备注：** 你也可以在 GitHub 上找到完成的版本 animals-table-fixed.html (see it live also).

为表格中的列提供共同的样式
-------------

### 不使用 <col> 应用样式

在我们继续介绍之前，我们将介绍本文中的最后一个功能。HTML 有一种方法可以定义整列数据的样式信息：就是 **`<col>`** 和 **`<colgroup>`** 元素。它们存在是因为如果你想让一列中的每个数据的样式都一样，那么你就要为每个数据都添加一个样式，这样的做法是令人厌烦和低效的。你通常需要在列中的每个 `<td>` 或 `<th>` 上定义样式，或者使用一个复杂的选择器，比如 `:nth-child()` (en-US)。

下面是一个简单的示例：

```html
<table>
  <tr>
    <th>Data 1</th>
    <th style="background-color: yellow">Data 2</th>
  </tr>
  <tr>
    <td>Calcutta</td>
    <td style="background-color: yellow">Orange</td>
  </tr>
  <tr>
    <td>Robots</td>
    <td style="background-color: yellow">Jazz</td>
  </tr>
</table>
```

下面就是上述代码的结果：

这样不太理想，因为我们不得不在列中的每个单元格中重复那些样式信息（在真实的项目中，我们或许会设置一个 `class` 包含那三个单元格，然后在一个单独的样式表中定义样式）。为了舍弃这种做法，我们可以只定义一次，在 `<col>` 元素中。`<col>` 元素被规定包含在 `<colgroup>` 容器中，而 `<colgroup>`就在 `<table>` 标签的下方。我们可以通过如下的做法来创建与上面相同的效果：

### 使用 <col> 应用样式

```html
<table>
  <colgroup>
    <col />
    <col style="background-color: yellow" />
  </colgroup>
  <tr>
    <th>Data 1</th>
    <th>Data 2</th>
  </tr>
  <tr>
    <td>Calcutta</td>
    <td>Orange</td>
  </tr>
  <tr>
    <td>Robots</td>
    <td>Jazz</td>
  </tr>
</table>
```

我们使用了两个 `<col>` 来定义“列的样式”，每一个 `<col>` 都会指定每列的样式，对于第一列，我们没有采取任何样式，但是我们仍然需要添加一个空的 `<col>` 元素，如果不这样做，那么我们的样式就会应用到第一列上，这和我们预想的不一样。

如果你想把这种样式信息应用到每一列，我们可以只使用一个 `<col>` 元素，不过需要包含 span 属性，像这样：

```html
<colgroup>
  <col style="background-color: yellow" span="2" />
</colgroup>
```

就像 `colspan` 和 `rowspan` 一样，`span` 需要一个无单位的数字值，用来指定让这个样式应用到表格中多少列。

### 动手练习：colgroup 和 col

又到了需要你自己独立完成的时间了。

下面你可以看到一位语言老师的时间表。星期五，她有一个新的课程，全天教荷兰语，但是在星期二和星期四的几个时间点，她也教德语。她想把那些包含她教学的日子的列高亮显示。

通过下面这些步骤来重构这个表格。

1. 首先，把 timetable.html 文件复制到你的本地环境。这个 HTML 文件包含你在上文中看到的表格，不过是减去样式信息的。
2. 在 table 的顶部添加一个 `<colgroup>` 元素，就放在 `<table>` 标签下面，`<colgroup>`可以添加 `<col>` 元素 (继续看下面剩余的步骤)。
3. 第一列和第二列不需要应用样式。
4. 为第三列添加一个背景颜色。`style` 属性是 `background-color:#97DB9A;`
5. 为第四列设置一个独立的宽度，`style` 属性是 `width: 42px;`
6. 为第五列添加一个背景颜色。`style` 属性是 `background-color: #97DB9A;`
7. 为第六列添加不同的背景颜色和边框，表示这是一个特殊的日子，表示她正在教一个新的课。 `style` 属性是 `background-color:#DCC48E; border:4px solid #C1437A;`
8. 最后两天是休息日，所以只需将它们设置为无背景颜色，但需要设置宽度；`style` 属性是 `width: 42px;`

看看你是否能完成这个示例，如果你遇到了困难，或想要核对你完成的作品，你可以在 GitHub 上找到完成的版本 timetable-fixed.html (也可以查看在线示例)。

小结
--

本章节仅仅包含了 HTML 表格的基础。在下一篇文章中，我们将介绍一些稍微更高级的表格功能，并开始考虑方便视力障碍的人士的访问。

* Overview: Tables
* 下一页


Add a hit map on top of an image - 学习 Web 开发

Add a hit map on top of an image
================================

现在我们将学习如何设置图像映射，先讨论他的缺点。

|  |  |
| --- | --- |
| 前提： |
 你应该已经知道如何create a basic HTML document
 以及add accessible images to a webpage. |
| 目标： | 学习如何将一张图片的不同区域链接到不同页面。 |

**警告：** 本文仅讨论客户端图像映射。不要使用服务器端图像映射，这需要用户拥有鼠标。

图像映射和它的缺点
---------

当你在`<a>`标签中嵌套图像，整个图像是链接到一个网页的。但在图像映射中，包含多个活动区域（称为“热点”），可以链接到不同的资源地址。

图像映射原本非常流行于导航策略，但是前提需要考虑它的性能和无障碍。

Text links (perhaps styled with CSS) 比图像映射更具优势：文本链接更轻量级、好维护、更易于 SEO，而且支持更多形式需求的访问（如，屏幕阅读器、纯文本浏览器、翻译服务等）。

如何正确的插入一张图像映射
-------------

### 步骤 1: 图片

不是所有图片都合适。

* 图片必须明确表明当用户跟随图片链接时会发生什么。`alt` 属性是必须的，但很多人注意不到。
* 图片必须明确指出热点的开始和结束位置。
* 在任何尺寸的视口下，热点都需要足够大，方便用户可以点击。多大足够呢？72 × 72 CSS pixels 是一个推荐的最小尺寸， 包括触摸目标之间额外的间隙。在 50languages.com (as of time of writing) 上的世界地图可以完美诠释这一点。用户点击 Russia 或 North America 要比 Albania 或 Estonia 容易得多。

插入图片的方式 和通常一样 (用 `<img>` 标签 和 `alt` 文本). 如果图片只是用作导航容器，你可以设置图片的 `alt=""`，改在后面 `<area>` 的 `alt` 中提供合适的文本。

你将需要一个特殊的 `usemap` 属性。为图像映射提供一个唯一标识，这个标识不能包含空格。将这个标识 (preceded by a hash) 作为 `usemap` 属性的值：

```html
<img src="image-map.png" alt="" usemap="#example-map-1" />
```

### 步骤 2: 激活你的热点

在这一步中，将所有代码放置 `<map>` 标签中。`<map>` 只需要一个属性，设置 `name` 和上面 `usemap` 属性一样的 map 值：

```html
<map name="example-map-1"> </map>
```

`在<map>` 元素中，我们需要嵌套 `<area>` 元素。一个 `<area>` 元素对应一个热点.。为保持键盘导航的直观，请确保`<area>` 的源顺序和视觉上的热点顺序一致。

`<area>` 元素是空元素，但是需要包含 4 个属性：

`shape` `coords`

`shape` 有 4 个值：`circle`, `rect`, `poly`, and `default`. ( `default` `<area>` 表示除去你定义的其他热点的剩余空间.) 根据你选择的形状需要在 `coords` 中提供对应的坐标信息。

* 对于 circle, 提供中心的 x、y 坐标，还需要提供半径。
* 对于 rectangle, 提供左上角和右下角的 x、y 坐标。
* 对于 polygon, 提供每个角的 x、y 坐标（至少 6 个值）。

坐标用 CSS 像素 px 表示。

In case of overlap, source order carries the day.

`href`

你需要链接的资源地址。如果你不希望当前区域链接到任何地方 (比方说，如果你正在创建一个空心圆)，你可以将这个属性保留为空。

`alt`

一个必选属性，告诉用户链接的指向或功能说明。`alt` 文本仅在图像不可用时显示。请参阅我们的guidelines for writing accessible link text。如果 `href` 属性为空并且整个图像已经具备了 `alt` 属性，则可以设置 `alt=""`。

```html
<map name="example-map-1">
  <area
 shape="circle"
 coords="200,250,25"
 href="page-2.html"
 alt="circle example" />

  <area
 shape="rect"
 coords="10, 5, 20, 15"
 href="page-3.html"
 alt="rectangle example" />
</map>
```

### 步骤 3: 确保它的可用范围

你还并没有完成除非你很严格的在多个浏览器和终端测试图像映射功能。尝试仅用键盘操作。尝试关掉图片。

如果你的图像映射宽度大于 240px，你将需要进一步调整来适应网站的响应式。仅调整小屏幕下的图片是不够的，因为不变的坐标无法适应这样的图像。

如果必须使用图像映射，你可以看看Matt Stow's jQuery plugin.。另外，Dudley Storey 示范了一种方法 use SVG for an image map effect，以及后来的ombined SVG-raster hack for bitmap images.

Learn more
----------

* `<img>`
* `<map>`
* `<area>`
* Online image map editor
* Advice on handling email clients


使用数据属性 - 学习 Web 开发

使用数据属性
======

HTML 是具有扩展性的设计，它初衷是数据应与特定的元素相关联，但不需要任何定义。data-\* 属性允许我们在标准、语义化的 HTML 元素中存储额外的信息，而不需要使用类似于非标准属性或者 DOM 额外属性之类的技巧。

HTML 语法
-------

语法非常简单。所有在元素上以`data-`开头的属性为数据属性。比如说你有一篇文章，而你又想要存储一些不需要显示在浏览器上的额外信息。请使用 data 属性：

```html
<article
 id="electriccars"
 data-columns="3"
 data-index-number="12314"
 data-parent="cars">
  ...
</article>
```

JavaScript 访问
-------------

在外部使用JavaScript去访问这些属性的值同样非常简单。你可以使用`getAttribute()`配合它们完整的 HTML 名称去读取它们，但标准定义了一个更简单的方法：`DOMStringMap`你可以使用`dataset`读取到数据。

为了使用`dataset`对象去获取到数据属性，需要获取属性名中`data-`之后的部分 (要注意的是破折号连接的名称需要改写为骆驼拼写法 (如"index-number"转换为"indexNumber"))。

```js
var article = document.querySelector("#electriccars");

article.dataset.columns; // "3"
article.dataset.indexNumber; // "12314"
article.dataset.parent; // "cars"
```

每一个属性都是一个可读写的字符串。在上面的例子中，`article.dataset.columns = 5`.将会调整属性的值为 5。

CSS 访问
------

注意，data 设定为 HTML 属性，他们同样能被CSS访问。比如你可以通过generated content使用函数`attr()`来显示 data-parent 的内容：

```css
article::before {
  content: attr(data-parent);
}
```

你也同样可以在 CSS 中使用属性选择器根据 data 来改变样式：

```css
article[data-columns="3"] {
  width: 400px;
}
article[data-columns="4"] {
  width: 600px;
}
```

你可以在这个JSBin 里看到全部演示。

Data 属性同样可以存储不断变化的信息，比如游戏的得分。使用 CSS 选择器与 JavaScript 去访问可以让你得到花俏的效果，这里你并不需要用常规的编写方案来编写代码。有关使用生成内容和 CSS 转换（JSBin 示例）的示例，请参阅此视频。

数据值是字符串。必须在选择器中引用数值才能使样式生效。

Issues
------

不要在 data attribute 里储存需要显示及访问的内容，因为一些其他的技术可能访问不到它们。另外爬虫不能将 data attribute 的值编入索引中。

IE 的支持度及显示效果是最主要讨论的问题。IE11+ 支持这个标准，但所有更早期的版本都不支持 dataset。为了支持 IE10 及以下的版本，你必须使用 `getAttribute()` 来访问。另外，读取 data-attributes 的行为相比 JS 存储数据会慢。使用 dataset 会比使用 getAttribute() 读取数据来得慢。

尽管如此，对于那些与元素相关的数据，这依然是一个很好的解决方案。

在 FireFox 49.0.2(其他版本也有可能) 中，javascript 将无法读出包含 1022 个及以上字符的 data 属性 (EcmaScript 4).

参见
--

* 该文章源自 Using data attributes in JavaScript and CSS on hacks.mozilla.org.
* 在 SVG 2 中也同样支持自定义 data 属性; 请参看`SVGElement.dataset` 和`data-\* (en-US)`.
* How to use HTML5 data attributes (Sitepoint)


编写快速加载的 HTML 网页的技巧 - 学习 Web 开发

编写快速加载的 HTML 网页的技巧
==================

以下技巧都是基于通用的知识和经验。

一个好的页面不仅要给访客提供一个更有交互性的站点，同时也需要降低你的服务器压力和网络请求。后者对于那些高访问量的站点，或在有爆炸性新闻出现等特殊情况下会出现流量突增的站点来说尤为关键。

页面加载性能的优化不仅仅是针对那些带宽有限的拨号上网或移动设备用户需要看的内容，对于提供给宽带用户访问的内容同样重要并且可以导致巨大的提升，哪怕对于那些拥有最快网速的访客。

技巧
--

### 减小页面的大小

直至今日，页面的大小仍是页面加载性能的最重要因素。

通过消除不必要的空白和注释（通常称为最小化）以及将内联脚本和 CSS 移至外部文件来减少页面大小，可以提高下载性能，而无需对页面结构进行其他改动。

诸如 HTML Tidy 这类的工具可以从有效的 HTML 源文件中自动截去行首空格和额外的空行，其他工具则可以通过重新格式化源代码来“压缩”JavaScript 或者通过混淆源码将长标识符替换为短标识符来减小文件大小。

### 最小化文件数量

减少一个页面引用的文件数量可以降低在下载一个页面的过程中需要的 HTTP 请求数量，从而减少这些请求的收发时间。

根据其缓存设置，浏览器可能会为每个所引用的文件发送一个带 `If-Modified-Since` 标头的请求给 web 服务器，以查询这些文件自上次加载后是否有被修改。查询引用文件上次修改时间会花费太多时间，导致网页首屏延迟，这是因为在渲染页面之前浏览器必须确认每个文件的修改时间。

如果你在 CSS 中经常使用背景图片，可以通过将图片合并为一张图片（即精灵图）来减少 HTTP 查找次数。然后，每次需要使用背景图片时，只需应用相同的图片，并适当调整 x/y 坐标即可。这种技术最适用于尺寸有限的元素，并不适用于所有背景图片的使用。不过，减少 HTTP 请求和单张图片缓存有助于缩短页面加载时间。

### 使用内容分发网络 CDN

在本文中，CDN 是一种缩短服务器与访问者之间物理距离的手段。随着服务器和访问者之间距离的增加，加载时间也会增加。假设你的网站服务器位于美国，而访问者来自印度，那么印度访问者的页面加载时间将远高于美国访问者。

CDN 是一个分布在不同地理位置的服务器网络，它们协同工作以缩短用户与网站之间的距离。CDN 存储网站的缓存版本，并通过离用户最近的网络节点向访问者提供这些版本，从而减少延迟 (en-US)。

进一步阅读：

* 理解 CDN
### 减少域名查询

每个独立的域名都会消耗 DNS 查询的时间，页面加载时间会随着独立域名数量、CSS 链接数量、JavaScript 还有图片资源的数量增加而增加。

这条可能算不上实用，然而，在你的页面中尽量少的使用来自不同域名的资源链接。

### 缓存重用的内容

确保任何内容可以被缓存，并且拥有一个合理的有效期。

尤其要注意 `Last-Modified` 标头。它可以实现高效的页面缓存；通过这个标头，可以向用户代理传达它想要加载的文件的相关信息，例如文件的最后修改时间。大多数 web 服务器会根据存储在文件系统中的最后修改日期，自动将 `Last-Modified` 标头附加到静态页面（如 `.html`、`.css`）上。对于动态页面（如 `.php`、`.aspx`），当然无法做到这一点，因此不会发送标头信息。

因此，特别是对于动态生成的页面，对这一主题进行一些研究是有益的。这可能会涉及到一些问题，但对于通常无法缓存的页面来说，这将会节省大量的页面请求。

更多信息：

1. HTTP Conditional Get for RSS Hackers
2. HTTP 304: Not Modified
3. 维基百科上的 HTTP ETag
4. HTTP 缓存
### 高效地排列页面组件

在页面最初显示时，会首先下载页面内容以及所需的 CSS 和 JavaScript，这样在页面加载时用户可以最快获得外观的反馈。由于内容通常都是文本，有利于在传输过程中压缩，从而为用户提供更快的响应。

任何需要在页面加载完成后才能使用的动态功能，都应先禁用，然后在页面加载完成后再启用。这将使 JavaScript 在页面内容之后加载，从而改善页面加载的整体外观。

### 减少内联脚本的数量

内联脚本对页面加载的影响很大，因为解析器必须假定内联脚本可能会在解析过程中修改页面结构。减少内联脚本的使用，尤其是减少使用 `document.write()` 来输出内容，可以提高页面的整体加载速度。为现代浏览器使用 DOM API 来操作页面内容，而不是使用 `document.write()`。

### 使用现代 CSS 和有效标记

使用现代 CSS 减少标记的用量，可以减少对（间隔）图片的需求。在布局方面，图片通常可以用具有样式的文本来替代，这样会“节省”许多资源。

使用合法标记还有其他优点。首先，浏览器在解释 HTML 时无需做错误校正（除了一些哲理性的问题，例如，是允许用户输入格式不一致，然后再用程序“校准”或统一化呢？还是加强约束规则，限制用户输入的格式？）。

再者，合法标记可以让那些给你的网站做预处理的工具功能最大化。例如，HTML Tidy 可以移除空白符和可选的结束标记；然而，在有严重的错误标记的网页中这些工具便无法工作。

### 给内容分块

使用 table 布局是一种不应该再采用的传统方法，而应运用浮动、定位、弹性盒或网格来布局。

当然，table 仍是不失为一种有效的展示表格数据的方式。为了帮助浏览器更快速的渲染你的页面，你应该避免嵌套 table。

相较于这种深度的嵌套：

```html
<table>
  <table>
    <table>
      …
    </table>
  </table>
</table>
```

用下图这样的非嵌套 table 或 div 会更好一些：

```html
<table>
  …
</table>
<table>
  …
</table>
<table>
  …
</table>
```

可参见 CSS 弹性盒布局和 CSS 网格布局 规范。

### 最小化和压缩 SVG 资源

大多数绘图应用程序生成的 SVG 通常包含不必要的元数据，这些元数据可以删除。配置服务器，对 SVG 资源应用 gzip 压缩。

### 最小化和压缩图片

大图像会导致页面加载时间延长。请考虑在将图片添加到页面之前对其进行压缩，使用 Photoshop 等图片处理工具内置的压缩功能，或使用 Compress Jpeg 或 Tiny PNG 等专门工具。

### 指定图像和表格的大小

如果浏览器能立即确定图像和表格的高度和/或宽度，就能显示网页而无需重新调整内容。这不仅能加快页面的显示速度，还能防止页面加载完成后出现令人讨厌的布局变化。因此，应尽可能为图片指定 `height` 和 `width`。

表格可以使用 CSS 选择器：综合性能：

```css
table-layout: fixed;
```

并且用用 `<col>` 和 `<colgroup>` 元素来指定列宽。

### 对图片使用懒加载

默认情况下，图片是**立即**加载的；也就是说，一旦图片在 HTML 中被处理，就会立即获取并渲染。所有立即加载的图片都会在窗口的 `load` 事件发送之前得到渲染。如果改用懒加载图像，浏览器就会暂缓加载图像，直到需要绘制可视视口 (en-US)时再加载。

要将图片标记为“懒加载”，可指定其 `loading` 属性的值为 `lazy`。这样设置后，只有在需要时才会加载图片。

```html
<img src="./images/footerlogo.jpg" loading="lazy" alt="MDN logo" />
```

请注意，`load` 事件触发时，懒加载的图片可能不可用。可以通过检查 `complete` (en-US) 布尔属性的值是否为 `true` 来确定给定图片是否已加载。

### 合理的选择用户代理

为使页面设计得到极大提升，应确保在工程中指定一个合理的用户代理（user agent）。不要奢求你的内容在所有浏览器中都完美的展现，特别是在那些低版本的浏览器中。

理想情况下，你的页面运行的最小环境要基于现代浏览器，这个浏览器起码要支持一些相关的标准。可以是最近版本的 Firefox、IE、谷歌 Chrome、Opera 和 Safari。

但要注意的是，本文列出的许多技巧都是常识性技术，适用于任何用户代理，并可应用于任何网页，与浏览器支持要求无关。

### 尽可能使用 async 和 defer

确保 JavaScript 脚本兼容 async (en-US) 和 defer (en-US)，任何时候都要尽可能使用 async (en-US)，特别是当你有较多的 script 标签时。

这样在加载 JavaScript 的过程中页面就不会重新绘制，否则，浏览器在不具有这些特性的 script 标签后就不会重绘任何东西。

注意：这些特性在页面第一次加载时会有所帮助，你可以这样用但不能指靠它在所有的浏览器中起作用。如果你遵循指南写出了非常优秀的 JavaScript 代码，也不必要再去修改它了。

页面结构示例
------

* ``<html>``
    + ``<head>``
        - ``<link>``
         CSS 文件用来修饰页面外观。在调试维护中把不相关的 CSS 拆分在不同文件中，且尽量减少文件的数量可以提高性能。
        - ``<script>``
         JavaScript 文件用来实现页面加载时**需要**的函数，而避免在页面加载后才能运行的 JavaScript。在调试维护中把不相关的 JavaScript 拆分在不同文件中，且尽量减少文件的数量可以提高性能。
    + ``<body>``
     用户能在完整页面下载完之前就可以看到的页面小分块（``<header>``/``<main>``/``<table>``）。
        - ``<script>``
         任何用于执行交互的脚本。交互脚本通常只能在页面完全加载并初始化所有必要对象后运行。没有必要在页面内容之前加载这些脚本。这只会减慢页面加载的初始外观。
         尽量减少文件数量以提高性能，同时将无关的 JavaScript 保存在单独的文件中以方便维护。
参见
--

* 书籍：Andy King 撰写的让你的站点访问更快
* 非常棒、讲的很全的加快站点访问的最佳实践资料（来自 Yahoo!）
* 用来分析和优化的工具：Google PageSpeed 工具


理解客户端 web 开发工具 - 学习 Web 开发

理解客户端 web 开发工具
==============

客户端工具可能让人望而生畏，不过这个系列的文章主要目的在于展示一些最常见的客户端工具的用途，阐明可以被你链接使用的工具，如何使用包管理器安装这些工具，以及如何使用命令行操控它们。我们将以一个完整的工具链示例收尾，示例会向你展示如何变得更具生产力。

**现在就从我们的“客户端工具概览”开始吧**

前提
--

在尝试使用这里列举的工具之前，你应该先好好学习一下 HTML, CSS, 和 JavaScript 的核心基础知识。

### 渴望成为一名前端开发人员？

我们已经把你实现目标所需要的所有必要信息都放在了一个课程里面。

开始

指南
--

1. 客户端工具概览

在这篇文章中，我们会简要介绍现代 web 工具：在 web 应用开发的生命周期中的何时使用何种工具，以及如何寻求帮助。

2. 命令行速成课程

毫无疑问，在开发过程中，你需要在终端（或者在“命令行”，它们其实是同一个东西）中运行一些命令。这篇文章将会提供对终端的介绍，包括一些你需要输入的基本命令，如何将命令链接在一起，以及何如添加你自己的命令行（CLI）工具。

3. 包管理基础

在这篇文章中，我们将会详细看一看包管理器，从而理解如何在我们自己的工程中使用它们——安装工程工具依赖、更新等等。

4. 一个完整的工具链

在这个系列的最后几篇文章中，我们将会带你构建一个工具链样例，从而帮你巩固前面学到的知识。我们将从设立一个实际的开发环境开始，一路在 Netlify 上相应的位置放置发布你的应用所需要的转换工具。在这篇文章里，我们将介绍一个设立我们的开发环境以及代码转换工具的例子。

5. 发布你的应用 (en-US)

在我们系列的最后一篇文章中，我们将会使用我们在前篇文章中构建的示例工具链，以便我们能够发布我们的样例应用。我们将代码推到 Github，使用 Netlify 进行发布，甚至还会向你展示如何在这个过程中加入一个简单的测试。


跨浏览器测试 - 学习 Web 开发

跨浏览器测试
======

本模块的重点是在不同的浏览器上测试 web 项目。我们研究确定你的目标受众（例如，你最需要担心哪些用户、浏览器和设备），如何去做测试，不同类型的代码中会面临的主要问题以及如何缓解这些问题，哪些工具在帮助你测试和解决问题方面最有用，以及如何使用自动化来加速测试。

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

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

**开始使用**

前提
--

在你尝试使用这里详细介绍的工具时，你需要已经学习核心 HTML、CSS 和 JavaScript 语言。

指南
--

跨浏览器测试简介

本文通过提供跨浏览器测试主题的概述，回答诸如“什么是跨浏览器测试？”、“将遇到的最常见类型的问题是什么？”和“什么是测试，识别和解决问题的主要方法？”等问题。

测试策略

接下来，我们深入了解测试，寻找目标受众（例如：你需要确定测试的浏览器，设备和其他部分），低成本测试策略（必要时对各种设备和一些虚拟机进行 adhoc 测试），更高科技策略（自动化，使用专用测试 app）和用户组测试。

处理常见的 HTML 和 CSS 问题

现在，我们着重考虑你在 HTML 和 CSS 代码中可能遇到的一些常见的跨浏览器问题，以及哪些工具可以用来防止问题发生和解决问题。这包括代码检查，处理 CSS 前缀，使用浏览器开发工具找出问题，使用 polyfill 添加浏览器支持，解决响应式问题等。

处理常见的 JavaScript 问题

现在我们来看看常见的跨浏览器 JavaScript 问题，以及怎么解决它们。这会包括使用浏览器开发工具跟踪和解决问题的信息，使用 polyfill 和库来解决问题，让现代的 JavaScript 特性在老浏览器中工作等等。

处理常见的无障碍问题

然后，我们把注意力转向无障碍上，提供常见问题的信息，怎么做简单的测试，和怎么使用审计/自动化工具找到无障碍问题。

实现特性检查

特性检查包括确定浏览器是否支持确定的代码块，据此运行不同的代码，让浏览器能一直正常工作，而不是在一些浏览器上崩溃/报错。本文详细介绍了如何编写简单的特性检查代码，怎么使用库来加速实现，利用一些像 `@supports` 的原生特性来进行特性检查。

自动测试简介

每天多次在几个浏览器和设备上手动运行可能会枯燥，浪费时间。有效处理这个问题，你需要熟悉自动化工具。在本文中，我们介绍可用的方法，怎么使用任务运行程序，以及学习如何使用商业浏览器自动化测试工具（如 Sauce Labs 和 Browser Stack）。

建立你自己的自动化测试环境

在本文中，我们会教你怎么安装你自己的自动化环境，并使用 Selenium / WebDriver 和测试库（如 selenium-webdriver for Node）运行自己的测试。我们还将介绍如何将本地测试环境与商业应用程序集成在一起，正如上一篇文章所讨论的。


Git 和 GitHub - 学习 Web 开发

Git 和 GitHub
============

所有开发者都将使用到**版本控制系统**（**V**ersion **C**ontrol **S**ystem，简称 **VCS**），这种工具让他们在分工合作时避免了不必要的重复与冲突，如果遇到什么问题，也可以及时回退到之前的版本。当今最流行的**版本控制系统**（至少在网络开发者中是这样的）是 **Git**，和与之关联的编程社区网站 **GitHub**。这篇短文将带你简单地了解他们。

主要介绍
----

版本控制系统在软件开发过程中是必不可少的：

* 我们很少独自完成一个项目，而在分工合作的同时我们都会有与他人的工作相冲突的风险：尤其是当两个人同时尝试修改同一段代码的时候。所以我们需要有相应的机制用以避免这种情况。
* 在开发一个项目的时候，我们希望能将代码及时保存，这样就可以避免像电脑突然崩溃辛苦全部白费这样的尴尬局面。
* 如果后期发现了问题，我们可能还会需要退回更早的版本。有的小朋友也许想到可以通过创建一堆 `myCode.js`、`myCode_v2.js`、`myCode_v3.js`、`myCode_final.js`、`myCode_really_really_final.js` 之类的文件用于保存历史版本，但这个方法不妥，容易出错。
* 不同的团队成员也会需要创建他们自己的独特的版本（在 Git 中叫做**分支**（branch）），他们在这里添加一些新的功能特性，然后通过一些可控的方法（在 GitHub 中我们使用**拉取请求**（pull request））将它们贡献到原来的主干项目中。

版本控制系统提供了能够满足以上需求的工具。Git 是版本控制系统的典范，而 GitHub 是一个为个人或团队操作 Git 仓库（repository）提供了 Git 服务器和一系列非常实用的工具的网站 + 基础设施。它提供了报告代码错误、检查工具以及分配任务和任务状态等项目管理工具等等。

**备注：** Git 实际上是一个分散式的版本控制系统，这意味你和其他所有人的电脑上都可以有一个这个复制了这个项目所有源代码的储存库的副本。你在自己的副本上进行了修改，然后提交给服务器，在那里将由这个项目的管理者来决定是否将你的修改添加到主本中。

事先准备
----

为了使用 Git 和 GitHub，你首先需要：

* 安装了 Git 的台式电脑（详见 Git 下载网址）。
* 用于使用 Git 的工具。你可以使用一个 Git 图形操作界面客户端（我们推荐 GitHub Desktop、SourceTree 或者 Git Kraken）或者也可以直接使用命令行，这取决于你的喜好。事实上，尽管你打算使用图形界面进行操作，了解一些基本的 Git 命令行指令也或许会比较有用。
* GitHub 账户。如果还没有的话，现在就通过这个链接去注册一个吧！

在此之前你并不一定要有关于网络开发、Git 和 GitHub 或者版本控制系统的任何知识。但是最好还是能有一点基础的计算机使用技能并且懂一点编程，以及有一些可以存放进储存库的代码。

建议你最好还能够有一些基础的命令行终端的知识，例如：切换目录、新建文件以及修改系统路径（`PATH`）。

**备注：** Github 并不是使用 Git 的唯一途径。还有很多像 GitLab 这样的选择值得你去尝试。你也可以尝试着去构建你自己的 Git 服务器来代替 Github 的功能。在这里我们只是将 Github 作为一种可行的途径进行介绍。

指南
--

先提醒一下这些链接将会带你去访问一些外部资源。我们最终将致力于开发我们专属的 Git 和 GitHub 教程，但现在，这些资料将会带你轻松入门。

Hello World（来自 GitHub）

这里是开始使用 GitHub 的正大门，在这里你可以学到 Git 的基础操作，例如：创建**仓库**和**分支** 、**创建提交**以及打开和合并**拉取请求**。

Git 使用手册（来自 GitHub）

这个 Git 使用手册就稍微更深入一点了，它解释了什么是**版本控制系统**、什么是**仓库**、**GitHub 模型**如何运行、**Git 指令**和示例等。

复刻项目（来自 GitHub）

当你想要对别人的代码做出贡献时，**复刻**（fork）项目是必不可少的步骤。这个指南将告诉你如何操作。

关于拉取请求（来自 GitHub）

这是一个管理拉取请求的有用的指南：你所修改的代码需要通过拉取请求来让项目的管理者决定是否采纳。

掌握议题（来自 GitHub）

**议题**（issue）就像是一个关于你这个 GitHub 项目的论坛，在这里人们可以提问题和报告错误，你可以管理更新（例如给人们分配需要解决的问题、澄清问题以及告知问题已经解决）。这篇文章会让你知道所需要的一些关于问题区的知识。

**备注：** 在 Git 和 GitHub 上面你还可以做一大堆事情，但我们认为，如果你想要有效地使用 Git，上面的这些知识是至少应该具备的。当你更深入地了解 Git 时，你将会意识到，当你开始使用更加复杂的指令时会更容易出错。但不要担心，即使是专业的网络工程师有时都会感到困惑，并通过网络检索或 Git 飞行规则和 Dangit, git! 这样的网站来寻找答案。

参见
--

* 理解 GitHub 流程
* Git 指令列表
* 掌握 Markdown 格式（在网页上、评论区常用的格式以及 `.md` 文件所使用的格式）。
* Github Pages 快速入门 （如何在 GitHub 上发布示例和网站）。
* 学习 Git 的分支结构
* Git 飞行规则 （在 Git 中实现特定功能的非常有用的方法介绍纲要，包括如何在出错时纠错等）。
* Dangit, git!（另一个十分有用的方法介绍纲要，特别是在出错的时候进行纠正的方法）。


理解客户端 JavaScript 框架 - 学习 Web 开发

理解客户端 JavaScript 框架
===================

JavaScript 框架是现代前端 web 开发的重要部分，为开发人员提供了构建可伸缩、交互式 web 应用程序的经过测试的工具。许多现代公司使用框架作为工具的标准部分，因此许多前端开发工作现在需要框架经验。

作为一个有抱负的前端开发人员，很难找出从哪里开始学习框架——有这么多不同的框架可供选择，新的问题出现，他们大多以类似的方式工作，但做一些不同的事情，还有一些具体的事情时要小心使用框架。

在这一系列文章中，我们旨在为你提供一个舒适的起点，帮助你开始学习框架。我们的目标不是详尽地教给你所有你需要知道的关于 React/ReactDOM，或者 Vue，或者其他一些特定的框架;框架团队自己的文档已经完成了这项工作。相反，我们想备份和首先回答更基本的问题，如：

* 为什么要使用框架？他们为我解决了什么问题？
* 当我尝试选择一个框架时，我应该问什么问题？我甚至需要使用框架吗？
* 框架有什么特性？它们一般是如何工作的？框架对这些特性的实现有何不同？
* 它们与“普通的”JavaScript 或 HTML 有什么关系？

在此之后，我们将提供一些教程，介绍一些主要框架的基本内容，为你提供足够的上下文和熟悉感，以便你自己开始更深入地学习。我们希望你以一种实用的方式来学习框架，不要忘记 web 平台的基本最佳实践，比如无障碍。

**通过“客户端框架介绍”即刻开始**

前提条件
----

在尝试学习客户端框架 (HTML、CSS，特别是 JavaScript) 之前，你应该首先真正学习核心 web 语言的基础知识。

如果你理解了框架底层的基础 Web 平台特性，你将能编写更加健壮和专业的代码，更容易地排除问题。

说明指导
----

1. 客户端框架介绍

我们首先从以下话题开始研究框架，框架领域的总览，JavaScript 和框架历史的简介，为什么需要框架以及框架能带给我们什么，如何考虑选择一个框架开始学习，有什么其他的客户端框架可供选择。

2. 框架的主要特性

每种 JavaScript 框架都有自己不同的方式更新 DOM，处理浏览器事件，为开发者提供愉悦的使用体验，这篇文章将探索“四大”框架的主要特性，从高级角度探讨框架的工作方式以及它们之间的区别。

React 教程
--------

**备注：** React 教程最后测试于 2020 年 5 月，基于 React/ReactDOM 16.13.1 和 create-react-app 3.4.1.

如果你需要确认你的代码与我们的版本区别，你可以在我们的todo-react repository仓库找到 React 应用示例代码的完整版本。想要获取当前最新的版本，查看https://mdn.github.io/todo-react-build/.

1. 开始使用 React

此章节我们将向 React 打招呼。我们将能发现有关 React 的背景和使用场景的一些细节，在我们的计算机上设置基本的 React 工具链，创建和运行简单的入门应用，学习 React 在这个过程是如何工作的。

2. 开始我们的 React 待办清单

假设我们计划使用 React 创建 proof-of-concept，一个允许用户添加，编辑和删除工作任务，不删除的情况标记任务完成的应用。此章节将带你完成基本的`App`组件结构和样式，为最后即将添加的独立组件定义和交互做好准备。

3. 组件化我们的 React 应用

到这个阶段，我们的应用仍是一个单一结构。在让它工作之前，我们需要将它分解为可管理的描述性组件。React 对于组件没有任何硬性规定--这完全取决于你！在此章节我们将向你展示一个合理的方式来将我们的应用分解成 组件。

4. React 交互：事件和状态 (en-US)

我们的组件化计划已经完成，现在是时候开始将我们的应用从一个完全静态的界面更新成允许交互和变更的界面。在这个章节我们将继续研究事件和状态，借此实现上述功能。

5. React 交互：编辑，过滤，条件渲染 (en-US)

我们已经接近 React 旅程的终点（至少目前是这样），我们将对我们的 Todo 列表应用的主要功能区域做最后的更改。这包括允许编辑现有的任务，在所有列表，已完成列表和未完成列表中过滤任务。在这个过程中我们将研究条件渲染。

6. React 的无障碍支持 (en-US)

在我们最后的教程章节，我们将重点介绍无障碍，包括 React 的焦点管理，这可以提高可用性，降低纯键盘用户和屏幕阅读器用户的困扰。

7. React 资源 (en-US)

我们的最后章节提供了一个 React 资源列表，供你用于进一步的学习。

Ember 教程
--------

**备注：** Ember 教程最后测试于 2020 年 5 月，基于 Ember/Ember CLI version 3.18.0.

如果你需要确认你的代码与我们的版本区别，你可以在ember-todomvc-tutorial repository找到 Ember 应用示例代码的完整版本。想要获取当前最新的版本，查看https://nullvoxpopuli.github.io/ember-todomvc-tutorial/ （这还包含了本教程未覆盖到的一些额外的特性）。

1. 开始使用 Ember (en-US)

在我们的 Ember 第一章节中我们将了解 Ember 的工作原理及其用途，本地安装 Ember 工具链，创建一个示例应用，然后做一些初始化设置以便开始开发。

2. Ember 应用结构和组件化 (en-US)

在此章节，我们将继续规划我们的 TodoMVC Ember 应用，为其添加 HTML 部分，随后将这些 HTML 分解为组件。

3. Ember 交互：事件，类和状态 (en-US)

至此，我们将添加一些交互行为到我们的应用，使其能够添加和显示新的待办事项。在此过程中，我们将研究如何在 Ember 中使用事件，创建包含 JavaScript 代码的组件类来控制交互功能，并且设置服务来跟踪我们的应用的数据状态。

4. Ember 交互：页脚功能，条件渲染 (en-US)

现在是时候开始在我们的应用中处理页脚功能了。这里我们将更新待办事项计数器以正确显示待完成待办事项的数量，并正确地为已完成事项应用样式（i.e. 对应列表项的复选框处于选中状态）。我们还将实装我们的“Clear completed”按钮。在这个过程中，我们将学习在我们的模板中使用条件渲染。

5. Ember 路由 (en-US)

在此章节我们学习路由，有时也称为 URL-based 过滤。我们将使用它来为三个待办视图------"All","Active"和"Completed"提供全局唯一的 URL。

6. Ember 资源和疑难解答 (en-US)

我们的最后一个 Ember 章节提供了一个供你进一步学习的资源列表，还有一些有用的疑难解答和其他信息。

Vue 教程
------

**备注：** Vue 教程最后测试于 2020 年 5 月，基于 Vue 2.6.11.

如果你需要确认你的代码与我们的版本区别，你可以在todo-vue repository找到 Vue 应用示例代码的完整版本。想要获取当前当前最新的版本，查看https://mdn.github.io/todo-vue/dist/.

1. 开始使用 Vue

现在让我们开始介绍 Vue，我们的第三个框架。在此章节我们将简单了解 Vue 的背景，学习如何安装 Vue 以及如何创建一个新项目，学习整个项目的高级架构以及独立的组件，了解如何在本地运行这个项目，并准备开始构建我们的示例。

2. 创建我们的第一个 Vue 组件

现在是时候深入 Vue，创建我们自己的自定义组件了--我们将从创建一个用来展示待办列表项目的组件开始。在这个过程中，我们将学习一些重要的概念例如在一个组件中调用另一个组件，通过 props 传递数据给另一个组件并保存数据状态。

3. 渲染 Vue 组件列表

至此我们已经得到了一个完全可用的组件，我们现在已经准备好添加多个`ToDoItem` 组件到我们的应用中了。在此章节我们将了解如何添加一组待办项数据到我们的`App.vue`组件，这组数据我们随后使用`v-for`指令将它们循环并显示到`ToDoItem`组件中。

4. 添加一个新的待办表单：Vue 事件，方法和模型

现在我们的应用已经有了示例数据和一个循环来将每条数据渲染到`ToDoItem`中。下一步我们要做的是让用户能够输入他们自己的待办事项到这个应用中。为此我们需要一个文本输入框`<input>`，一个在数据提交时触发的事件，一个在数据提交时被触发来添加数据和渲染列表的方法和一个用来控制数据的模型。这就是这个章节我们将要介绍的内容。

5. 使用 CSS 美化 Vue 组件

终于到了美化我们的应用的时间了。在此章节我们将探索用 CSS 美化 Vue 组件的不同方法。

6. 使用 Vue 计算属性

在这个章节我们将添加一个显示已完成待办项目数量的计数器，使用 Vue 的一个叫做计算属性的特性。计算属性与方法相似，但是只有它的依赖内容变更时才会再次运作。

7. Vue 条件渲染：编辑既有待办项

现在是时候加入我们还未实现一个主要功能部分了------编辑既有待办项。为了实现这个功能，我们将利用 Vue 的条件渲染能力------`v-if`和`v-else`来使既有待办项目在查看状态和文本编辑状态之间切换。我们还将添加删除待办项的功能。

8. 使用 Vue refs 进行焦点管理

我们快要完成 Vue 的使用了。最后要看的一个功能是焦点管理，换种说法，如何改善我们的应用的键盘可操作性。我们将解决这个问题，通过 Vue refs------一个允许你直接操作虚拟 DOM 的底层 DOM，或在组件中直接操作其子组件内部 DOM 结构的高级特性。

9. Vue 资源

我们将提供一个你可以用来进一步学习的资源列表来圆满结束我们的 Vue 学习教程，从这个列表中你也可以获取一些有用的技巧。

我们应该选择哪个框架？
-----------

我们在这里发布专注于主要三大框架的系列说明文章------React/ReactDOM，Ember 和 Vue 的一些原因如下：

* 它们在今后的一段时间内都将是最受欢迎的选择------就像使用任何软件工具一样，依附于一个处于活跃开发状态并且不太会突然停止开发的工具是一个明智的选择，并且它们会成为你寻找工作时所需的技能之一。
* 它们拥有强大的社区和良好的文档支持。学习复杂的主题时得到帮助是很重要的，特别是你是刚开始学习时。
* 我们没有资源能覆盖所有现代框架。维护所有现代框架的最新列表是非常困难的，因为总有新的框架在不断被创造出来。
* 作为一个初学者，在大量的可选内容中选择关注哪部分是很难的，维持一个相对短的列表反而是有益的。

我们想在前面说一下------我们**没有**因为我们认为它们是最好的或者我们赞同它们而选择这些我们正在关注的框架。我们只是认为它们在上面这些方面评价比较高。

值得一提的是我们也希望在我们的初版发布时能够包含更多框架的内容，但是我们最终决定先把初版内容发布出来随后在此基础上再慢慢添加更多框架内容，而不是延后更长的时间。如果你最喜欢的框架没有包含在这部分内容中而且你想帮助改善这个情况，请立即告诉我们！你可以通过Matrix或Discourse联系我们或者发送邮件到我们的邮件列表mdn-admins list。


实现特性检测 - 学习 Web 开发

实现特性检测
======

* 上一页
* Overview: Cross browser testing
* 下一页

特性检测涉及到确定浏览器是否支持某个代码块，并根据是否支持（或不支持）运行不同的代码，从而使浏览器能够始终提供工作体验，而不是在某些浏览器中崩溃/出错。本文详细介绍了如何编写自己的简单特性检测，如何使用库来加速实现，以及特性检测的本地功能，如 `@supports`。

|  |  |
| --- | --- |
| 前提： |
 熟悉核心的 HTML、CSS
 以及 JavaScript 语言，对跨浏览器测试原则有充分了解。
  |
| 目标： | 了解特性检测的概念，并能够在 CSS 和 JavaScript 中实现合适的解决方案。 |

特性检测的概念
-------

特性检测的思想是，你可以运行一个测试来确定当前浏览器是否支持某个特性，然后有条件地运行代码，以便在*支持*该特性的浏览器和*不支持*该特性的浏览器中提供可接受的体验，如果不这样做，在不支持你在代码中使用的特性的浏览器中将无法正确地显示你的网站，从而产生糟糕的用户体验。

让我们回顾一下，看看我们在处理常见的 JavaScript 问题中提到的例子——地理位置 API（它暴露了网络浏览器所运行设备的可用位置数据）有其主要入口点。全局 Navigator 对象上有一个 `geolocation` 属性。因此，你可以通过使用类似以下的方法来检测浏览器是否支持地理位置：

```js
if ("geolocation" in navigator) {
  navigator.geolocation.getCurrentPosition(function (position) {
    // 使用如 Google Maps API 显示地图上的位置
  });
} else {
  // 为用户提供静态地图的选择
}
```

要检测一个单一的特性，`if` 语句效果很好。对于复杂的应用，考虑使用一个成熟的特性检测库，而不是自己编写。Modernizr 是特性检测测试的工业标准，我们将在后面讲述一下。

在我们继续之前，先说一件事——不要把特性检测和**浏览器嗅探**混淆起来（检测什么特定的浏览器在访问网站）——这是一种糟糕的做法，应该不惜一切代价加以阻止。参见使用不良的浏览器嗅探代码以了解更多细节。

撰写自己的特性检测代码
-----------

在本节中，我们将研究如何在 CSS 和 JavaScript 中实现自己的特性检测测试。

### CSS

你可以通过在 JavaScript 中测试 *element.style.property*（例如 `paragraph.style.rotate`）的存在来编写 CSS 特性测试。

一个经典的例子可能是测试浏览器是否支持 Subgrid (en-US)；对于支持 `grid-template-columns` 和 `grid-template-rows` 的 subgrid 值的浏览器，我们可以在布局上使用 subgrid。对于不使用 subgrid 的浏览器，我们可以使用普通的网格，它可以正常工作，但没有那么酷的外观。

以此为例，如果支持该值，我们可以包含一个 subgrid 样式表，如果不支持，则包含一个普通网格样式表。为此，我们可以在 HTML 文件的头部包含两个样式表：一个是所有的样式表，另一个在不支持 subgrid 的情况下实现默认布局：

```html
<link href="basic-styling.css" rel="stylesheet" />
<link class="conditional" href="grid-layout.css" rel="stylesheet" />
```

这里，`basic-styling.css` 处理所有我们想给每个浏览器的样式。我们还有两个 CSS 文件，`grid-layout.css` 和 `subgrid-layout.css`，它们包含了我们想根据浏览器的支持程度有选择地应用的 CSS。

我们使用 JavaScript 来测试对 subgrid 值的支持，然后根据浏览器的支持情况更新我们的条件样式表的 `href`。

我们可以在我们的文档中添加 `<script></script>` 元素，填充以下 JavaScript：

```js
const conditional = document.querySelector(".conditional");
if (CSS.supports("grid-template-columns", "subgrid")) {
  conditional.setAttribute("href", "subgrid-layout.css.css");
}
```

在我们的条件语句中，我们使用 `CSS.supports()` 测试 `grid-template-columns` 属性是否支持 `subgrid` 值。

#### @supports

CSS 有一个原生的特性检测机制：`@supports` at-规则。这与媒体查询的工作方式类似，只是它不是根据分辨率、屏幕宽度或长宽比等媒体特性选择性地应用 CSS，而是根据是否支持某个 CSS 特性来选择性地应用 CSS，类似于 `CSS.supports()`。

例如，我们可以使用 `@supports` 重写我们之前的例子：

```css
@supports (grid-template-columns: subgrid) {
  main {
    display: grid;
    grid-template-columns: repeat(9, 1fr);
    grid-template-rows: repeat(4, minmax(100px, auto));
  }

  .item {
    display: grid;
    grid-column: 2 / 7;
    grid-row: 2 / 4;
    grid-template-columns: subgrid;
    grid-template-rows: repeat(3, 80px);
  }

  .subitem {
    grid-column: 3 / 6;
    grid-row: 1 / 3;
  }
}
```

只有当前浏览器支持 `grid-template-columns: subgrid;` 声明时，这个 at-规则块才会应用里面的 CSS 规则。为了使一个带值的条件生效，你需要包含一个完整的声明（而不仅仅是一个属性名称），并且不包括结尾的分号。

`@supports` 也支持 `AND`、`OR` 和 `NOT` 逻辑——如果 subgrid 选项不可用，另一个块应用常规网格布局：

```css
@supports not (grid-template-columns: subgrid) {
  /\* 这里写其他规则 \*/
}
```

这比前面的例子更方便，我们可以在 CSS 中进行所有的特性检测，不需要 JavaScript；而且我们可以在一个 CSS 文件中处理所有的逻辑，减少 HTTP 请求。由于这个原因，它是确定浏览器对 CSS 特性支持的首选方法。

### JavaScript

我们在前面已经看到了一个 JavaScript 特性检测测试的例子。一般来说，这种测试是通过几种常见的模式之一完成的。

但请记住，有些特性是已知不可检测的，见 Modernizr 在 2016 年发布的不可检测的列表。

对象的成员

检查一个特定的方法或属性（通常是使用 API 的入口或你正在检测的其他特性）是否存在于其父 `Object` 中。

我们前面的例子使用这种模式来检测 Geolocation 的支持，通过测试 `navigator` 对象的 `geolocation`成员：

```js
if ("geolocation" in navigator) {
  // 访问 navigator.geolocation API
}
```

元素的属性

使用 `Document.createElement()` 在内存中创建一个元素，然后检查其上是否存在属性。

这个例子展示了一种检测 Canvas API 支持的方法：

```js
function supports\_canvas() {
  return !!document.createElement("canvas").getContext;
}
if (supports\_canvas()) {
  // 创建 canvas 元素并在其上作画
}
```

**备注：** 上例中的双非运算符（`!!`）是一种将返回值强制转换为“适当的”布尔值的方法，而不是真值/假值，后者可能使结果偏离。

方法在元素上的特定返回值

使用 `Document.createElement()` 在内存中创建一个元素，然后检查该元素上是否存在一个方法。如果有的话，检查它的返回值。请参阅深入了解 HTML 视频格式检测中的特性测试，了解这种模式的一个例子。

元素保留分配的属性值

使用 `Document.createElement()` 在内存中创建一个元素，将一个属性设置为特定值，然后检查该值是否被保留。关于这种模式的例子，请参见深入了解 HTML <input> 类型检测中的特性测试。

#### matchMedia

现在，我们还想提到 `Window.matchMedia` 这个 JavaScript 特性。这是一个允许你在 JavaScript 内部运行媒体查询测试的属性。它看起来像这样：

```js
if (window.matchMedia("(max-width: 480px)").matches) {
  // 在这里运行 JavaScript
}
```

举个例子，我们的 Snapshot 演示利用它来有选择地应用 Brick JavaScript 库，用它来处理 UI 布局，但只适用于小屏幕布局（480px 宽或以下）。我们首先使用 `media` 属性，仅在页面宽度为 480px 或更小的情况下，将 Brick CSS 应用于页面：

```css
<link href="dist/brick.css" rel="stylesheet" media="all and (max-width: 480px)">
```

然后我们在 JavaScript 中多次使用 `matchMedia()`，只在小屏幕布局时运行 Brick 导航特性（在宽屏幕布局中，所有东西都可以一次看到，所以我们不需要在不同的视图之间导航）。

```js
if (window.matchMedia("(max-width: 480px)").matches) {
  deck.shuffleTo(1);
}
```

使用 Modernizr 实现特性检测
-------------------

使用上述技术实现自己的特性检测测试是可能的。然而，你也可以使用一个专门的特性检测库，因为它使事情变得更加简单。所有特性检测库之母是 Modernizr，它可以检测你所需要的一切。现在让我们来看看如何使用它。

当你在尝试使用 Modernizr 时，你不妨使用开发版，它包括所有可能的特性检测测试。现在就来下载吧：

1. 点击 Development build 链接。
2. 点击页面上方的粉色大型 *Build* 按钮。
3. 在出现的对话框中，点击顶部的 *Download* 链接。

把它保存在合理的地方，就像你在本系列其他指南那样使用一个目录。

当你在生产环境中使用 Modernizr 时，你可以到你已经访问过的下载页面，点击加号按钮，只包括你需要的特性检测。然后，当你点击 *Build* 按钮时，你将下载一个只包含这些特性检测的自定义构建，使文件大小更小。

### CSS

让我们来看看 Modernizr 在选择性应用 CSS 方面是如何工作的。

1. 首先，复制提供的 `supports-feature-detect.html` 和 `supports-styling.css` 文件中的内容。将它们保存为 `modernizr-css.html` 和 `modernizr-css.css`。
2. 更新 HTML 中的 `<link>` 元素，使其指向正确的 CSS 文件（你也应该将 `<title>` 元素更新为更合适的内容！）：

```html
<link href="modernizr-css.css" rel="stylesheet" />
```

3. 在这个 `<link>` 元素上面，添加一个 `<script>` 元素，将 Modernizr 库应用到页面，如下图所示。这需要在任何可能使用它的 CSS（或 JavaScript）之前应用到页面上。

```html
<script src="modernizr-custom.js"></script>
```

4. 现在编辑你的 `<html>` 起始标签，使它改变成以下这样：

```html
<html lang="en-us" class="no-js">
  …
</html>
```

此时，试着加载你的页面，你会对 Modernizr 在 CSS 特性方面的工作有一个概念。如果你看一下浏览器开发者工具的 DOM 检查器，你会发现 Modernizr 已经像这样更新了你的 `<html>` 元素的 `class` 属性：

```html
<html
 class="js no-htmlimports no-proximity sizes no-flash transferables applicationcache blobconstructor blob-constructor no-contextmenu (and loads of more values)">
  …
</html>
```

它现在包含了大量的类，表示对不同技术特性的支持状态。举例来说，如果浏览器完全不支持 grid，`<html>` 将被赋予 `no-cssgrid` 的类名。如果你在类列表中搜索，你还会看到其他与网格有关的类，比如：

* `cssgridlegacy` 或 `no-cssgridlegacy`，取决于是否支持传统版本的网格。

**备注：** 你可以找到大部分类名的含义列表——见 Modernizr 检测的特性。

不幸的是，Modernizr 并没有测试对一些新的 CSS 特性的支持，如容器查询、层叠层或子网格。如果它这样做了，我们将以如下方式更新我们的 `@supports` 例子：

```css
main {
  display: grid;
  grid-template-columns: repeat(9, 1fr);
  grid-template-rows: repeat(4, minmax(100px, auto));
}

.item {
  display: grid;
  grid-column: 2 / 7;
  grid-row: 2 / 4;
  grid-template-rows: repeat(3, 80px);
}

/\* 支持 subgrid 的浏览器的属性 \*/
.csssubgrid .item {
  grid-template-columns: subgrid;
}

.csssubgrid .subitem {
  grid-column: 3 / 6;
  grid-row: 1 / 3;
}

/\* 不支持 subgrid 的浏览器的回落 \*/
.no-csssubgrid .subitem {
  display: flex;
  flex: 33%;
}
```

那么，这是如何工作的呢？因为所有这些类名都被放在了 `<html>` 元素上，你可以使用特定的后代选择器来针对那些支持或不支持某个特性的浏览器。所以在这里，我们只对支持 subgrid 的浏览器应用最上面的规则，而对不支持的浏览器应用最下面的规则（`no-csssubgrid`）。

**备注：** Modernizr 的所有 HTML 和 JavaScript 特性测试都以类名的形式报告，因此，如果需要，你可以根据浏览器是否支持 HTML 或 JavaScript 特性，有选择地应用 CSS。

### JavaScript

Modernizr 也同样为实现 JavaScript 特性检测做了充分准备。它通过使全局的 `Modernizr` 对象对它所应用的页面可用，它包含了作为 `true`/`false` 属性的特性检测结果。

例如，在浏览器中加载我们的 `modernizr-css.html` 示例，然后试着在你的 JavaScript 控制台中输入 `Modernizr.`，然后再输入其中的一些类名（这里也一样）。比如：
```

Modernizr.flexbox
Modernizr.xhr2
Modernizr.fetch
```

控制台将返回 `true`/`false` 值，表示你的浏览器是否支持这些特性。

让我们看一个示例来说明你如何使用这些属性。

1. 首先，将 `modernizr-js.html` 示例文件复制到本地。
2. 使用 `<script>` 元素将 Modernizr 库附加到 HTML 中，正如我们在以前的演示中所做的那样。把它放在现有的 `<script>` 元素上面，该元素将谷歌地图 API 附加到页面上。
3. 接下来，在第二个 `<script>` 元素中的 `YOUR-API-KEY` 占位符文本（就像现在这样）填写一个有效的谷歌地图 API 密钥。要获得密钥，请登录谷歌账户，进入获取密钥/认证页面，然后点击蓝色的*获取密钥*按钮并按照说明操作。
4. 最后，在 HTML 主体的底部（就在 `</body>` 标签之前）再添加一个 `<script>` 元素，并在标签内放入以下脚本：

```js
if (Modernizr.geolocation) {
  navigator.geolocation.getCurrentPosition(function (position) {
    let latlng = new google.maps.LatLng(
      position.coords.latitude,
      position.coords.longitude,
    );
    let myOptions = {
      zoom: 8,
      center: latlng,
      mapTypeId: google.maps.MapTypeId.TERRAIN,
      disableDefaultUI: true,
    };
    let map = new google.maps.Map(
      document.getElementById("map\_canvas"),
      myOptions,
    );
  });
} else {
  const para = document.createElement("p");
  para.textContent = "啊，没有地理位置支持！";
  document.body.appendChild(para);
}
```

试试你的例子吧！这里我们使用 `Modernizr.geolocation` 测试来检查当前浏览器是否支持地理定位。如果支持，我们就运行一些代码，获取你的设备的当前位置，并将其绘制在谷歌地图上。

总结
--

这篇文章合理地涵盖了特性检测的细节，通过主要的概念，向你展示了如何实现你自己的特性检测测试和使用 Modernizr 库来更容易地实现测试。

接下来，我们将了解一下自动测试。

* 上一页
* Overview: Cross browser testing
* 下一页


进行测试的策略 - 学习 Web 开发

进行测试的策略
=======

* 上一页
* Overview: Cross browser testing
* 下一页

这篇文章主要是讨论如何进行跨浏览器测试，回答一些比较常见的疑惑，譬如：“什么是跨浏览器测试？”，“会遇到哪些常见的问题？”以及“如何测试、区分以及修复问题？”

|  |  |
| --- | --- |
| 准备： |
 熟练掌握 HTML,
 CSS, 和
 JavaScript 语言; 了解跨浏览器测试的核心概念。
  |
| 目标： | 了解跨浏览器测试所涉及的高级概念。 |

是否需要测试？
-------

开始跨浏览器测试（cross browser testing）之前你需要确定哪些浏览器将进入被测试名单。测试所有用户可能使用的浏览器或移动设备是不可能的——数量太大，而且浏览器和设备总在不断更新。

可行的替代方案是，尝试确保你的网站适用于最常见的浏览器和设备，然后进行防御性编码，以便为你的网站提供最广泛的支持范围。

防御性编码的实质是一种智能回退措施：如果某个功能或样式在浏览器中不起作用，该网站能够将其降级为不太令人兴奋的后备方案，但仍然能提供可接受的用户体验——主要目的是保证网站可以访问，即使它看起来逊色一点。

本章的目的是建立一个供你在测试时参考的浏览器/设备图表。你可以根据需要将其设置成简单或复杂版本——常见的方法是具有多个等级的支持，例如：

1. A 级别：常见/现代浏览器（仍在广泛使用）：需要彻底测试并提供全功能支持。
2. B 级别：较旧/功能少的浏览器（少数用户还在使用）：测试并提供更基础的体验，使得核心内容和服务能够完整访问。
3. C 级别：罕见/未知浏览器：不需要测试，假设网站内容可以显示。在我们的防御性编码起效的情况下，用户可以访问到网页的全部内容。

在以下各节中，我们将以此等级为参考构建支持图表。

**备注：** 雅虎最先使这一方法流行起来，详情参见《浏览器分级支持系统》（Graded browser Support**）。**

### 有根据地假设

你可以称之为“假设”或“直觉”。这不是一种准确、科学的方法，但作为具有网络行业经验的人，你应该要清晰地了解你所想测试的一些浏览器，这将形成一个良好的初级浏览器分级支持系统。

例如，如果你居住在西欧或北美，你会发现许多人使用 Windows 和 Mac 的台式机或笔记本电脑，常用浏览器是 Chrome，Firefox，Safari，IE 和 Edge。你可以测试前三个浏览器的最新版本，因为它们会定期更新。对于 Edge 和 IE，你应当测试它们近期的几个版本。以上这些浏览器都属于 A 级别。

**备注：** 你一次只能在计算机上安装一个版本的 IE 或 Edge，因此你可能必须使用虚拟机或其他方法来执行所需的测试。稍后请参阅虚拟机部分。

很多人使用 iOS 和 Android，因此你可能还想测试最新版本的 iOS Safari，最近几个版本的 Android stock 浏览器，以及适用于 iOS 和 Android 的 Chrome 和 Firefox。理想情况下，你应该在手机和平 板电脑上测试它们，以确保响应式设计正常运行。

你也许知道很多人仍然在使用 IE 9，它版本老旧而且功能较少，所以我们把它列为 B 级别。

到目前为止，我们整理了以下浏览器支持级别：

1. A 级别：Windows / Mac 的 Chrome 和 Firefox，Mac 的 Safari，Windows 的 Edge 和 IE（它们的最后两个版本），iPhone / iPad 的 iOS Safari，手机/平板电脑的 Android stock 浏览器（最后两个版本），手机和平板电脑上的 Chrome 和适 Android 的 Firefox（最后两个版本）。
2. B 级别：Windows 的 IE 9
3. C 级别：无

如果你居住在其他地方，或者正在开发其他地方（例如某些国家或地区）的网站，那你可能需要对不同的常用浏览器进行测试。

**备注：** “我老板用 Blackberry，所以我们最好确保它看起来很好”也是一个很有说服力的理由。

### 浏览器份额统计

有一个很有用的方法来确定你要测试的浏览器的范围，那就是浏览器份额统计数据。有许多网站提供这些统计数据，例如：

* Netmarketshare
* Statcounter

这些数据都偏向于北美，并不是特别准确，不过它们可以让你了解大致趋势。

例如，让我们转到 Netmarketshare。你可以看到 Opera 的使用人数很少但仍然有人使用，所以我们应该将其作为 C 级别添加到支持图表中。

IE8 被列为重要，但它较老且不再更新。Opera Mini 也很重要，但在运行复杂的 JavaScript 方面，它的能力并不强（有关详细信息，请参阅 Opera Mini and JavaScrip）。我们应该把它放到 B 级别。

### 使用分析工具

使用像 Google Analytics 这样的分析网站来获取更准确的数据。它能够为你提供诸如人们在用什么浏览器来浏览你的网站这样的精准的统计数据。当然，这需要你已经有一个网站在使用它，所以它不太适合新上线的网站。

但是，析历史记录可用于查找支持统计信息以影响公司网站的新版本或要添加到现有站点的新功能。如果你有这些可用，它们比上述全球浏览器统计信息更准确。

#### 配置 Google 分析

1. 首先，你需要一个谷歌帐户。使用此帐户可登录Google Analytics。
2. 选择 Google Analytics (web)) 选项，然后单击"注册"按钮。
3. 在注册页面中输入你的网站/应用详细信息。这是相当直观的设置;获得正确的最重要的字段是网站 URL。这需要是你的网站/应用的根 URL。
4. 填写完所有内容后，按"获取跟踪 ID"按钮，然后接受显示的服务条款。
5. 下一页为你提供一些代码段和其他说明。对于基本网站，你需要做的是复制网站跟踪代码块，并将其粘贴到你要在网站上使用 Google Analytics 跟踪的所有不同页面。你可以在关闭`</body>`标记下方，也可以位于其他适当位置，以防止其与应用程序代码混淆。
6. 将更改上载到开发服务器，或将代码上载到其他任何位置。

就是这样！你的网站现在应该准备好开始报告分析数据。

#### 学习分析数据

现在，你应该能够返回 Analytics Web主页，并开始查看你收集的有关网站的数据 (当然，你需要留出一点时间才能真正收集一些数据)。

默认情况下，你应该看到报告选项卡，如下所示：

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies/analytics-reporting.png)

有大量的数据，你可以看看使用谷歌分析––自定义报告在不同的类别，等等––我们没有时间讨论这一切。 Getting started with Analytics 为初学者提供了一些有关报告 (以及更多) 的有用指导。

还应鼓励你查看左侧的不同选项，并查看你可以找到哪些类型的数据。例如，你可以通过从左侧菜单中选择" *Audience > Technology > Browser & OS*"来了解用户正在使用的浏览器和操作系统。

**备注：** 使用 Google 分析时，你需要提防误导性偏见，例如"我们没有 Firefox 移动用户"可能会导致你不必为 Firefox 移动提供支持。但是，你不会有任何火狐移动用户，如果该网站被打破在火狐手机摆在首位。

### 其他注意事项

你可能还应包括其他注意事项。你绝对应该将辅助功能作为 A 级测试要求 (我们将在处理常见辅助功能问题一文中介绍你应该测试的内容)

此外，你可能还有其他注意事项。如果要创建某种公司 Intranet，以便向经理提供销售数据，并且例如，所有经理都提供了 Windows 手机，则可能需要将移动 IE 支持作为优先事项。

### 最终的支持图表

因此，我们的最终支持图表最终将如下所示：

1. A 级：适用于 Windows/Mac 的 Chrome 和 Firefox、适用于 Mac 的 Safari、Windows 的"边缘"和"IE"(每个版本的最后两个版本)、iPhone/iPad 的 iOS Safari、手机/平板电脑上的 Android 股票浏览器 (最后两个版本)、适用于 Android 的 Chrome 和 Firefox(最后两个版本) 在手机平板电脑上。通过常见测试的无障碍。
2. B 级：IE 8 和 9 用于 Windows，Opera Mini。
3. C 级：Opera，其他合适的现代浏览器。
你想要测试什么？
--------

当你的代码库有需要测试的新添加项时，在开始测试之前，应编写出需要通过才能接受的测试要求列表。这些要求可以是可视的，也可以是功能性的，两者结合起来，成为可用的网站功能。

思考下面的例子 (查看源码，在线预览):

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies/sliding-box-demo.png)

此功能的测试标准可以这样编写：

A 级和 B 级：

* 按钮应该由用户的主要控制机制激活，不管它是什么 ––这应该包括鼠标、键盘和触摸
* 切换按钮应使信息框显示/消失。
* 文本应可读。
* 使用屏幕阅读器的视障用户应该能够访问文本。

A 级：

* 信息框在出现/消失时应平滑地动画。
* 渐变和文本阴影应显示为增强框的外观。

You may notice from the text in the example that it won't work in IE8 — this is a problem according to our support chart, which you'll have to work on, perhaps by using a feature detection library to implement the functionality in a different way if the browser doesn't support CSS transitions (see Implementing feature detection, later on in the course).你可能会从示例中的文本中注意到它在 IE8 中不起作用 ––根据我们的支持图表，这是一个问题，你必须处理这个问题，如果浏览器不采用其他方式，则可能使用功能检测库以不同的方式实现功能（如果浏览器不支持 CSS 转换的话）(请参阅实现功能检测，稍后将在本课程中)。

你可能还注意到，该按钮仅使用键盘无法使用，这也需要纠正。也许我们可以使用一些 JavaScript 来实现一个键盘控件的切换，或者完全使用一些其他的方法？

这些测试条件很有用，因为：

* 当你执行测试时，它们会为你提供一组要遵循的步骤。
* 在执行测试时，可以轻松地将它们转换为用户组要遵循的指令集 (e.g. "try to active the button using your mouse, and then the keyboard...")–– 请参阅下面的 用户测试。
* 它们还可以为编写自动测试提供基础。如果你确切知道要测试的内容以及成功条件是什么 (请参阅使用自动化工具自动执行浏览器测试，请稍后在系列中) 编写此类测试。
建立一个测试实验室
---------

执行浏览器测试的一个选项是自己进行测试。为此，你可能需要使用实际物理设备和模拟环境的组合 (使用仿真器或虚拟机)。

### 真实设备

通常最好让一个实际的设备运行你要测试的浏览器 , 这在行为和整体用户体验方面提供了最高的准确性。对于合理的低级设备实验室，你可能需要以下内容：

* Mac，安装了你需要测试的浏览器，包括火狐、Chrome、Opera 和 Safari。
* 安装了需要测试的浏览器的 Windows 电脑，其中可能包括 Edge (or IE), Chrome, Firefox 和 Opera。
* 更高规格的 Android 手机和平板电脑，安装了浏览器，你需要测试 - 这可能包括 Chrome，火狐，和 Opera Mini 的 Android，以及原来的 Android stock 浏览器。
* 更高规格的 iOS 手机和平板电脑，并安装了你需要测试的浏览器 , 这可能包括 iOS Safari，以及适用于 iOS 的 Chrome、Firefox 和 Opera Mini。

如果可以获取这些选项，则以下是不错的选项：

* 可用的 Linux PC ,以防你需要测试特定于 Linux 版本的浏览器的错误。Linux 用户只使用火狐、Opera 和 Chrome。如果只有一台计算机可用，则可以考虑在单独的分区上创建运行 Linux 和 Windows 的双引导计算机。Ubuntu 的安装程序使设置起来非常简单;有关此帮助，请参阅WindowsDualBoot。
* 几个低规格的移动设备，因此你可以测试低功率处理器上的动画等功能的性能。

你的主工作计算机也可以用于安装其他工具以用于特定目的，例如辅助功能审核工具、屏幕阅读器和仿真器/虚拟机。

一些大型公司拥有设备实验室，这些实验室库存了大量不同的设备，使开发人员能够在非常具体的浏览器/设备组合上查找 Bug。较小的公司和个人通常负担不起如此复杂的实验室，因此倾向于使用较小的实验室、仿真器、虚拟机和商业测试应用程序。

我们将介绍下面的其他选项。

**备注：** 已做出一些努力用来创建可公开访问的设备实验室，请参阅 Open Device Labs。

**备注：** 我们还需要考虑辅助功能 — 可以在计算机上安装许多有用的工具，以方便辅助功能测试，但我们将在本课程的后面部分介绍"处理常见辅助功能问题"一文中的工具。

### 模拟器

仿真器基本上是在计算机内运行并模拟某种设备或特定设备条件的程序，允许你比查找要测试的特定硬件/软件组合更方便地执行某些测试。

仿真器可能与测试设备条件一样简单。例如，如果要对宽度/高度媒体查询进行一些快速而粗劣的测试以进行响应式设计，则可以使用 Firefox 的Responsive Design Mode。Safari 也有类似的模式，可以通过访问“\*Safari > 首选项”\*和"显示开发"菜单，然后选择"开发">"输入响应式设计模式"来启用。Chrome 也有类似的功能：设备模式 (请参阅Simulate Mobile Devices with Device Mode)。

不过，你经常必须安装某种仿真器。要测试的最常见设备/浏览器如下所示：

* 用于开发 Android 应用程序的官方Android Studio IDE对于仅测试 Google Chrome 或旧版 Android 浏览器上的网站来说有点沉重，但它确实附带了一个强大的emulator。如果你想要更轻巧的东西，LeapDroid是 Windows 的一个很好的选择，Andy 是一个合理的选择，可以在 Windows 和 Mac 上运行。
* Apple 提供了一个名为Simulator 的应用程序，它运行在XCode开发环境之上，并模拟 iPad / iPhone / Apple Watch / Apple TV。这包括本机 iOS Safari 浏览器。不幸的是，这只能在 Mac 上运行。

你也经常可以为其他移动设备环境找到模拟器，例如：

* Blackberry （适用于 Windows，Mac OSX 和 Linux 的模拟器）。
* 如果要进行测试，可以单独模拟Opera Mini。
* 适用于 Windows Mobile 操作系统的仿真器：请参阅Windows Phone Emulator for Windows Phone 8 和Test with the Microsoft Emulator for Windows 10 Mobile（这些仅适用于 Windows）。

**备注：** 许多模拟器实际上需要使用虚拟机（见下文）;在这种情况下，通常提供指令，和/或将虚拟机的使用合并到仿真器的安装程序中。

### 虚拟机

虚拟机是在台式计算机上运行的应用程序，允许你运行整个操作系统的仿真，每个操作系统都划分在自己的虚拟硬盘驱动器中（通常由主机硬盘上存在的单个大文件表示）。有许多流行的虚拟机应用程序可用，例如Parallels，VMWare和Virtual Box;我们个人喜欢后者，因为它是免费的。

**备注：** 你需要可用的大容量硬盘空间来运行虚拟机模拟;模拟的每个操作系统都会占用大量内存。你可能倾向于为每次安装选择所需的硬盘空间;你也可能会试图侥幸使用 10GB 空间，但会被建议使用 50GB 空间或更多，以便让操作系统运行可靠。大多数虚拟机应用程序提供的一个很好的选择是创建一个动态分配（\*\*dynamically allocated）\*\*的硬盘驱动器，它会随着需要的增长和缩小而动态改变。

要使用虚拟盒，你需要：

1. 获取要模拟的操作系统的安装程序磁盘或映像（例如 ISO 文件）。Virtual Box 无法提供这些;大多数，如 Windows 操作系统，是无法自由分发的商业产品。
2. 下载适用于你的操作系统的相应安装程序并进行安装。
3. 打开应用程序;你将看到如下视图：
 ![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies/virtualbox.png)
4. 要创建新虚拟机，请按左上角的“新建”按钮。
5. 按照说明进行操作，并根据需要填写以下对话框。你会：
    1. 为新虚拟机提供名称
    2. 选择要在其上安装的操作系统和版本
    3. 设置应分配多少 RAM（我们建议使用 2048MB 或 2GB）
    4. 创建虚拟硬盘（在包含立即创建虚拟硬盘，VDI（虚拟磁盘映像）和动态分配的三个对话框中选择默认选项）。
    5. 选择虚拟硬盘的文件位置和大小（选择一个合理的名称和位置来保留它，并且大小指定大约 50GB，或者你可以轻松指定）。

现在，新的虚拟框应出现在 Virtual Box UI 主窗口的左侧菜单中。此时，你可以双击它以打开虚拟机––它将开始启动虚拟机，但它还没有安装操作系统。此时，你需要将对话框指向安装程序映像/磁盘，它将运行在虚拟机上安装它的步骤，就像它是真正的计算机一样。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies/virtualbox-installer.png)

**警告：** 你需要确保此时要在虚拟机上安装要安装的操作系统映像，然后立即安装。如果此时取消该进程，它可能会使虚拟机无法使用，并使其成为你需要删除它并再次创建它。这不是致命的，但令人讨厌。

完成此过程后，你应该有一台虚拟机在主机窗口内运行操作系统。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Testing_strategies/virtualbox-running.png)

你需要像处理任何实际安装一样处理此虚拟操作系统安装 - 例如，安装要测试的浏览器，安装防病毒程序以保护其免受病毒侵害。

拥有多个虚拟机非常有用，特别是对于 Windows IE / Edge 测试 - 在 Windows 上，你无法并排安装多个版本的默认浏览器，因此你可能需要构建一个虚拟机库来处理根据需要进行不同测试，例如：

* Windows 10 with Edge 14
* Windows 10 with Edge 13
* Windows 8.1 with IE11
* Windows 8 with IE10
* Windows 7 with IE9
* Windows XP with IE8
* Windows XP with IE7
* Windows XP with IE6

**备注：** 虚拟机的另一个好处是虚拟磁盘映像是相当独立的。如果你正在团队中工作，则可以创建一个虚拟磁盘映像，然后复制并传递它。如果它是许可产品，只需确保你拥有运行所有 Windows 副本或正在运行的任何其他副本所需的许可证。

### 自动化和商业应用

正如上一章所述，通过使用某种自动化系统，你可以从浏览器测试中减少很多痛苦。你可以设置自己的测试自动化系统（Selenium是首选的流行应用程序），它确实需要一些设置，但是当你解决问题时可能会相当受益。

还有一些商业工具，如Sauce Labs和Browser Stack，可以为你做这种事情，如果你愿意在测试中投入一些资金，则无需担心设置的问题。

我们将在后续文章中查看如何使用此类工具。

用户测试
----

在我们继续之前，我们将通过谈论用户测试来完成本文––如果你有一个愿意用户组来测试你的新功能，这可能是一个很好的选择。请记住，这可以像你希望的那样廉价又有效––你的用户群可以是一群朋友，一群同事，或一群无偿或有偿志愿者，这取决于你是否有钱花在测试上。

通常，你可以让用户在某种开发服务器上查看包含新功能的页面或视图，这样你就不会在完成之前放置最终站点或更改。你应该让他们按照一些步骤报告他们得到的结果。提供一组步骤（有时称为脚本）非常有用，这样你就可以获得与你尝试测试的内容相关的更可靠的结果。我们在上面的 你想要测试什么 部分中提到了这一点––很容易将其中详述的测试标准转换为要遵循的步骤。例如，以下内容适用于有视力的用户：

* 在台式计算机上使用鼠标单击问号按钮几次。刷新浏览器窗口。
* 使用台式计算机上的键盘选择并激活问号按钮几次。
* 在触摸屏设备上点击几次问号按钮。
* 切换按钮应使信息框显示/消失。在上述三种情况中，是否都这样做？
* 文字可读吗？
* 信息框在出现/消失时是否能够流畅地生成动画？

运行测试时，最好还是：

* 尽可能设置单独的浏览器配置文件，禁用浏览器扩展和其他此类操作，并在该配置文件中运行测试（例如，请参阅Use the Profile Manager to create and remove Firefox profiles和Share Chrome with others or add personas）
* 在可用的情况下运行测试时使用浏览器的私有模式功能（例如，Firefox 中的Private Browsing，Chrome 中的Incognito Mode），因此不会保存 cookie 和临时文件等内容。

这些步骤旨在确保你正在测试的浏览器尽可能“纯粹（pure）”，即没有安装任何可能影响测试结果的内容。

**备注：** 如果你有可用的硬件，另一个有用的低功耗选项是在低端手机/其他设备上测试你的网站 - 随着网站变得更大并且具有更多效果，网站放慢速度的可能性更高，因此你需要开始给予表现更多考虑。尝试在低端设备上运行你的功能将使更高端设备的体验更有可能。

**备注：** 某些服务器端开发环境提供了有用的机制，可以将站点更改部署到仅部分用户，从而提供了一种有用的机制，可以在不需要单独的开发服务器的情况下获取由用户子集测试的功能。示例：Django Waffle Flags。

总结
--

阅读本文后，你现在应该知道如何识别目标受众/目标浏览器列表，然后在该列表上有效地执行跨浏览器测试。

接下来，我们将把注意力转向测试可能发现的实际代码问题，从 HTML 和 CSS 开始。

* 上一页
* Overview: Cross browser testing
* 下一页


解决常见的无障碍问题 - 学习 Web 开发

解决常见的无障碍问题
==========

* 上一页
* Overview: Cross browser testing
* 下一页

接下来，我们将关注无障碍，提供关于一些常见问题的信息，如何进行简单测试以及如何使用审核/自动化工具来查找无障碍问题。

|  |  |
| --- | --- |
| 前提： |
 熟悉HTML,
 CSS,和JavaScript语言; 理解跨浏览器测试原理 |
| 目标： | 能够诊断常见的无障碍问题，并使用适当的工具和技术解决问题。 |

什么是无障碍？
-------

当我们在 web 技术的背景下说无障碍时，大多数人立即想到确保残疾人可以使用网站/应用程序，例如：

* 视障人士使用屏幕阅读器或放大/缩放浏览文本
* 有运动功能障碍的人使用键盘（或其他非鼠标功能）使用网站。
* 有听力障碍的人依赖于替代音频/视频内容的文本。

但是，说无障碍仅与残疾人有关是错误的。实际上，无障碍的目的是使你的网站/应用程序在尽可能多的环境中被尽可能多的人使用，而不仅仅是那些使用高性能台式计算机的用户。极端的例子可能包括：

* 移动端用户
* 使用其他浏览设备（例如电视，手表等）
* 使用较旧设备的用户（他们可能没有最新的浏览器）
* 设备性能不高的用户（他们可能具有较慢的处理器）

在某种程度上，本模块都是关于无障碍的 — 跨浏览器测试可确保你的网站可以被尽可能多的人使用。这篇无障碍是什么？ 更全面透彻地定义了什么是无障碍。

也就是说，本文将涵盖跨浏览器和有关残疾人的测试问题以及他们如何使用 Web。我们在其他地方已经讨论过其他领域，例如响应式设计问题和性能。

**备注：** 就像 Web 开发中的许多事情一样，无障碍不是 100% 的成功或失败可以定义的；对于所有内容而言，几乎不可能实现 100％的无障碍，尤其是当站点变得越来越复杂时。我们更多的是通过防御性编码并遵循最佳实践，努力使尽可能多的人可以访问尽可能多的内容。

常见无障碍问题
-------

在本节中，我们将围绕 Web 无障碍，详细介绍与特定技术相关的一些主要问题、要遵循的最佳实践，以及可以进行的一些快速测试，以查看你的网站是否朝着正确的方向发展。

**备注：** 无障碍在道德上是正确的事情，对企业也有好处 (残疾用户，移动用户等构成了重要的细分市场), 并且在世界许多地方，提供出来的网络媒体资源无法为残疾人服务也是违法的。阅读无障碍指南和法律获取更多相关信息。

### HTML

HTML 语义化 (语义化正确地使用 HTML 标签) 对于无障碍来说是开箱即用的 — 这类内容可供无视障人士阅读（前提是你不会做任何愚蠢的事情，例如使文本变小或使用 CSS 隐藏它），也可被屏幕阅读器（从字面上读出网页的应用）之类的辅助技术使用，并赋予其他优势。

#### 语义化结构

HTML 语义化最重要的捷径是为你的内容使用标题和段落的结构；这是因为屏幕阅读器用户倾向于将文档标题用作导航，以更快地找到他们需要的内容。如果你的内容没有标题，那么他们将获得的是一大坨文字，没有任何可定位的标记。坏的例子和好的例子如下：

```html
<font size="7">My heading</font> <br /><br />
This is the first section of my document.
<br /><br />
I'll add another paragraph here too.
<br /><br />
<font size="5">My subheading</font>
<br /><br />
This is the first subsection of my document. I'd love people to be able to find
this content!
<br /><br />
<font size="5">My 2nd subheading</font>
<br /><br />
This is the second subsection of my content. I think is more interesting than
the last one.
```

```html
<h1>My heading</h1>

<p>This is the first section of my document.</p>

<p>I'll add another paragraph here too.</p>

<h2>My subheading</h2>

<p>
  This is the first subsection of my document. I'd love people to be able to
  find this content!
</p>

<h2>My 2nd subheading</h2>

<p>
  This is the second subsection of my content. I think is more interesting than
  the last one.
</p>
```

此外，你的内容应该在逻辑顺序上讲得通的 — 你总能在以后再为它们写 CSS，但你应该在一开始就确定内容正确的顺序。

作为测试，你可以关闭网站的 CSS，然后看看没有了 CSS 网站是否能被理解。你可以通过从代码中删除 CSS 来手动完成此操作，但是最简单的方法是使用浏览器功能，例如：

* Firefox: 选择*查看 > 页面样式 > 无样式*
* Safari: 选择*开发 > 停用样式* (需要开启“开发”菜单，点击*Safari > 偏好设置 > 高级 > 在菜单栏中显示“开发”菜单*)
* Chrome: 安装 Web Developer Toolbar 扩展程序，然后重启浏览器。点击图标，选择*CSS > 停用所有样式*
* Edge: 选择*查看 > 样式 > 无样式*

#### 使用键盘

某些 HTML 功能可以使用键盘来选择 — 这是默认的，从早期 web 开始就是这样的。具有此功能的元素是允许用户与网页交互的常见元素，比如 links, `<button>`s, 以及表单元素，比如`<input>`.

浏览native-keyboard-accessibility.html (查看源码) 尝试一下— 在新标签页中打开它，然后尝试按 Tab 键；按下几下后，你应该看到标签焦点开始在不同的可聚焦元素之间移动；在每个浏览器中，被聚焦的元素都被赋予突出的默认样式 (不同的浏览器表现略有不同) 以便你能分辨聚焦在哪个元素上。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/button-focused-unfocused.png)

然后，你可以按 Enter / Return 键来关注焦点链接，或者按一个按钮（我们已经包含一些 JavaScript 来使按钮提醒消息），或者在输入框开始输入文本，（其他表单元素具有不同的控件，例如`<select>`元素可以使用向上和向下箭头键显示和循环显示其选项）。

请注意，不同的浏览器可能具有不同的键盘控制选项。大多数现代浏览器都遵循上述的标签模式（你也可以执行 Shift + Tab 来向后移动可聚焦元素），但是某些浏览器具有自己的特性：

* Firefox for the Mac doesn't do tabbing by default. To turn it on, you have to go to *Preferences > Advanced > General*, then uncheck "Always use the cursor keys to navigate within pages". Next, you have to open your Mac's System Preferences app, then go to *Keyboard > Shortcuts*, then select the *All Controls* radio button.
* Safari 默认情况下不允许你按 tab 遍历链接；要启用此功能，你需要打开 Safari 的“*偏好设置*”，转到“*高级*”，然后选中“*按下 Tab 键以高亮显示网页上的每一项*”。

**警告：** 你应该在所写的任何新页面上执行这种测试 — 确保可以通过键盘使用功能。

这个例子强调了正确使用语义元素的重要性。可以用 CSS 将任何元素的样式设置为看起来像链接或按钮，并使用 JavaScript 让其表现为像链接或按钮一样，但实际上它们不是链接或按钮，你将失去很多语义化元素带给你的无障碍。因此，尽量避免这样做。

另一个技巧 — 如我们的示例所示，你可以使用:focus伪类控制可聚焦元素在聚焦时的外观。最好将焦点和悬停样式加重显示，这样无论是使用鼠标还是键盘的用户，都能直观地察觉控件在被激活时将执行的操作

```css
a:hover,
input:hover,
button:hover,
select:hover,
a:focus,
input:focus,
button:focus,
select:focus {
  font-weight: bold;
}
```

**备注：** 如果你决定使用 CSS 删除默认的焦点样式，请确保将其替换为更适合你的设计的其他样式 — 这是一种非常有价值的无障碍工具，不应删除。

#### 模拟键盘

有时可能无法使用键盘完成无障碍。你可能有一个语义不是很好的网站（也许你最终得到了一个糟糕的 CMS 网页，该 CMS 生成了由<div> 组成的按钮），或者你正在使用一个没有内置键盘无障碍的复杂控件，例如 HTML5 `<video>` 元素（令人惊奇的是，Opera 是唯一允许你在<video>元素的默认浏览器控件之间进行制表的浏览器）。你有几种选择：

1. 使用<button>元素（默认情况下都是可以在 button 间使用 Tab 键）和 JavaScript 创建自定义控件，以连接其功能。有关此示例，请参见Creating a cross-browser video player (en-US)。
2. 通过 JavaScript 创建键盘快捷键，因此当你按键盘上的某些键时，功能被激活。请参阅Desktop mouse and keyboard controls (en-US)，以获取一些可用于任何目的（比如游戏）的例子。
3. 使用一些有趣的策略来伪造按钮行为。以我们的fake-div-buttons.html示例为例（查看源码）。这里我们通过为每个假按钮赋予属性`tabindex="0"`（请参阅 WebAIM 的tabindex 文章以获取更多详细信息），使假的<div>按钮能够被聚焦（包括通过制表符）。这使我们可以跳到按钮上，但不能通过回车键激活它们。为此，我们必须添加以下 JavaScript 代码：

```js
document.onkeydown = function (e) {
  if (e.keyCode === 13) {
    // The Enter/Return key
    document.activeElement.onclick(e);
  }
};
```

在这里，我们向`document`对象添加了一个监听器，以检测何时按下了键盘上的按钮。我们通过事件对象的 keyCode 属性检查按下了什么按钮；如果它是与回车键匹配的键码，则使用`document.activeElement.onclick()`运行存储在按钮的 onclick 处理程序中的函数。`document.activeElement`为我们提供了当前页面上被聚焦的元素。

**备注：** 仅当你通过事件处理程序属性（例如`onclick`）设置原始事件处理程序时，此技术才有效。`addEventListener`将无法正常工作。重新构建功能会有很多额外的麻烦。并且肯定还有其他问题。最好能从根源解决问题，使用正确的语义化元素。

#### 替代文本

替代文本对于无障碍非常重要 — 如果一个人有视觉或听觉障碍使他们无法看到或听到某些内容，那么这就是一个问题。可用的最简单的文本替代方法是`alt`属性，我们应该在所有包含相关内容的图像上包括该属性。其中应包含对图像的描述，该描述可在页面上成功传达其含义和内容，并由屏幕阅读器读取并读出给用户。

**备注：** 更多信息请阅读替代文本。

可以通过多种方法来测试缺少的替代文本，例如，使用无障碍审计工具。

对于视频和音频内容，Alt 文本稍微复杂一些。有一种方法可以定义文本轨道（例如，字幕）并在播放视频时以 `<track>` 元素和 WebVTT 格式的形式显示它们（请参见向 HTML 视频中添加字幕相当好，但是如果你想提供音频的替代文本或支持较旧的浏览器，则在页面某处或单独页面上显示一个简单的文本记录可能是个好主意。

#### 元素关系和上下文

HTML 中有某些功能和最佳实践，旨在提供元素之间的上下文和关系。三个最常见的示例是链接，表单标签和数据表。

使用屏幕阅读器的人们通常会使用一项共同的功能，即他们会拉出页面上所有链接的列表。在这种情况下，链接文本需要脱离上下文。例如，标记为“单击此处”，“单击此处”等的链接列表确实对无障碍不利。链接文本最好在上下文和上下文之外都有意义。

表单`<label>`元素是允许我们使表单可访问的主要功能之一。表单的麻烦在于，你需要标签来说明应在每个表单输入中输入哪些数据。每个标签都必须包含在`<label>`内，以将其明确链接到其对应的表单输入框（属性值的每个`<label>`的`for`属性值必须与表单元素`id`值匹配），即使源顺序不是完全合乎逻辑的，也能提供很好的无障碍。

**备注：** 更多关于链接文本和表单标签，请阅读有意义的文字标签

最后，简要介绍一下数据表。基本数据表可以用非常简单的标记编写（请参阅bad-table.html和源码)），但这存在问题 — 屏幕阅读器用户无法将行或列作为数据分组关联在一起，但你需要知道标题行是什么，以及标题行行的标题还是列的标题等。这些只能从可视化的表格才能知道。

相反，如果你看一下我们的`punk-bands-complete.html`示例（示例，源码），则可以在此处看到一些无障碍辅助，例如表头（`<th>`和`作用域`属性），`<caption>`元素等。

**备注：** 更多信息，请阅读可访问的表格

### CSS

CSS 往往提供的基本无障碍功能要比 HTML 少得多，但是如果使用不当，它仍然会对无障碍造成同样的损害。下面是一些涉及 CSS 的无障碍的点：

* 使用正确的语义元素修饰 HTML 中的不同内容；如果要创建不同的视觉效果，请使用 CSS-不要滥用 HTML 元素来获得所需的外观。例如，如果你想要更大的文本，请使用`font-size`，而不是`<h1>` (en-US)标签。
* 确保内容顺序在没有 CSS 的情况下有意义；你可以随时使用 CSS 设置页面样式。
* 你应该确保按钮和链接之类的交互元素具有适当的聚焦/悬停/活动状态设置，以便用户能姨一目了然。如果出于风格原因删除默认设置，请确保包括一些替代的样式。

还有其他几个需要注意的地方。

#### 颜色和颜色对比度

为你的网站选择配色方案时，应确保文本（前景）颜色与背景颜色形成鲜明对比。你的设计可能看起来很酷，但是如果视力障碍者（例如色盲）无法阅读你的内容，那就不好了。使用 WebAIM 的Color Contrast Checker之类的工具来检查你的方案是否有足够对比度。

另一个提示是不要仅依靠颜色来表示界标/信息，因为这对于看不见颜色的人来说是不好的。例如，不要将所需的表单字段标记为红色，而应使用星号和红色标记它们。

**备注：** 高对比度也可以让使用带有光滑屏幕的设备（例如智能手机或平板电脑）的人在明亮的环境（例如阳光）下可以更好地阅读页面。

#### 隐藏的内容

在许多情况下，视觉设计要求并非一次显示所有内容。例如，在我们的Tabbed info box example“示例（查看源码）中，我们有三个信息面板，但是我们将它们放在彼此的顶部，用户可以通过单击以显示每个选项卡（也可以通过键盘访问 — 可以使用 Tab 键和回车键选择它们）。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/20191022144107.png)

屏幕阅读器用户根本不关心这些，只要源内容顺序有意义，他们就很满意，并且他们可以全部获取。绝对定位（在本示例中使用的定位）通常被视为隐藏内容以产生视觉效果的最佳机制之一，因为它不会阻止屏幕阅读器获取相关内容。

另一方面，你不应该使用`visibility``:hidden`或者`display``:none`, 因为它们会让屏幕阅读器取不到那些内容，除非你真的想让屏幕阅读器不读取那些内容。

**备注：** Invisible Content Just for Screen Reader Users有更多关于这个话题的详细信息

### JavaScript

就无障碍而言，JavaScript 具有与 CSS 相同的问题 — 如果使用不当或使用过度，无障碍可能会很糟糕。我们已经提到了与 JavaScript 相关的一些无障碍问题，主要是在 HTML 语义化那块 — 你应该始终使用适当的语义化 HTML 来在合适的地方实现功能，例如，适当地使用链接和按钮。尽量不要使用 JavaScript 代码中结合`<div>`元素来伪造功能 — 容易出错，并且比直接使用 HTML 标签还要做更多的工作。

#### 简单的功能

通常，简单的功能只应使用 HTML 标签完成 — JavaScript 仅用于增强功能，而不能用于实现简单功能。好的 JavaScript 用法包括：

* 提供客户端表单验证，可在不等待服务器检查数据的情况下，迅速向用户发出有关表单条目问题的警报。如果不能在客户端验证，则该表格仍然可以使用，但验证速度可能会较慢。
* 提供全键盘用户可访问的 HTML5 `<video>`的自定义控件（如我们之前所述，默认浏览器视频控件在大多数浏览器中均无法通过键盘访问）。

**备注：** WebAIM 的Accessible JavaScript提供了一些关于 JavaScript 无障碍注意事项的信息。

更复杂的 JavaScript 实现可能会带来无障碍问题 — 你需要尽力而为。例如，期望让使用 WebGL 编写的复杂 3D 游戏对盲人来说 100％无障碍是不合理的，但是你可以实现键盘控件，以便非鼠标用户可以使用它，并使配色方案具有足够的对比度供有颜色分辨障碍的人使用。

#### 复杂的功能

无障碍存在问题的主要领域之一是复杂的应用程序，其中涉及复杂的表单控件（例如日期选择器）和动态内容，内容会经常增量更新。

非自带的复杂的表单控件可能会存在问题，因为它们往往涉及大量嵌套的`<div>`，浏览器默认情况下不知道如何处理它们。如果你自己开发控件，则需要确保它们可以通过键盘访问。如果你使用的是某种第三方框架，请在开始使用之前仔细检查可用的选项以了解它们是否具有完备的无障碍。例如，Bootstrap 就对无障碍有相当好的支持（尽管 Rhiana Heath 的Making Bootstrap a Little More Accessible探讨了它的一些问题（主要与色彩对比度有关），并着眼于一些解决方案）。

定期更新的动态内容可能会成为问题，因为屏幕阅读器用户可能会错过这些内容，尤其是在意外更新的情况下。如果你有一个包含主要内容面板的单页应用程序，该应用程序使用XMLHttpRequest或Fetch定期更新，那么屏幕阅读器用户可能会错过这些更新。

#### WAI-ARIA

你是否需要使用这种复杂的功能，或者使用普通的旧语义化 HTML 代替？如果确实需要复杂性，则应考虑使用WAI-ARIA（Accessible Rich Internet Applications - 可访问的互联网应用），该规范为诸如复杂的表单控件和更新面板之类的项目提供了语义（以新的 HTML 属性形式），这样大部分浏览器和屏幕阅读器就能理解内容。

要处理复杂的表单窗口小部件，你需要使用 ARIA 属性（例如`roles`）来声明窗口小部件中不同元素的角色（例如，它们是选项卡还是选项卡面板？），用`aria-disabled`来表示控件是否禁用等。

要处理内容定期更新的区域，可以使用`aria-live`属性，该属性标识更新区域。它的值指示屏幕阅读器如何处理更新内容：

* `off`: 默认值。更新内容不被读出。
* `polite`: 当用户空闲时读出更新内容。
* `assertive`: 尽快读出更新内容。
* `rude`: 直接读出更新内容，即使会打断用户正常阅读。

例子如下：

```html
<p><span id="LiveRegion1" aria-live="polite" aria-atomic="false"></span></p>
```

浏览 Freedom Scientific 的ARIA (Accessible Rich Internet Applications) 动态更新区域示例 — 高亮显示的段落每 10 秒更新一次内容，屏幕阅读器应将此内容读出给用户。ARIA Live Regions - Atomic是另一个很有用的例子。

我们这里没有足够的空间来详细介绍 WAI-ARIA，你可以在WAI-ARIA basics了解更多。

无障碍测试工具
-------

现在，我们已经介绍了不同 Web 技术的无障碍注意事项，包括一些测试方法（例如键盘导航和颜色对比度检查器），让我们看一下在进行无障碍测试时可以使用的其他工具。

### 审计工具

你可以使用许多审计工具检查你的网页，这些审计工具将检查它们并返回页面上存在的无障碍问题列表。一些审计工具：

* Tenon: 一个相当不错的在线应用程序，它通过提供的 URL 遍历代码，并返回有关无障碍错误的结果，包括度量标准，特定错误以及它们影响的 WCAG 标准以及建议的修复方式。
* tota11y: Khan Academy 的无障碍工具，采用 JavaScript 库的形式，你可以将其附加到页面上，以提供许多无障碍工具。
* Wave: 另一个在线无障碍测试工具，它接受网址并返回该页面带注释的视图，其中高亮显示了无障碍问题。

看下面的例子，我们用的是 Tenon。

1. 访问Tenon 主页。
2. 使用bad-semantics.html示例测试，输入链接地址并按下*Analyse Your Webpage（译者注：开始分析你的网页）。*
3. 下滑，直到你看到错误/描述部分，如下图。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/tenon-screenshot.png)

你还可以探索一些选项（请参阅页面顶部附近的\*Show Options（译者注：显示选项）\*链接），或者使用 Tenon 的 API。

**备注：** 这些工具不足以单独解决你的所有无障碍问题。你需要将这些，知识和经验，用户测试等结合起来才能获得完整的解决方案。

### 自动化工具

Deque's aXe tool比我们上面提到的审计工具更优秀。和其他工具一样，它检查页面并返回无障碍错误。它很有用，可以提供浏览器扩展程序：

* aXe for Chrome
* aXe for Firefox

扩展程序将无障碍选项卡添加到浏览器开发人员工具。例如，我们安装了 Firefox 版本，然后使用它来审核bad-table.html示例。我们得到以下结果：

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/axe-screenshot.png)

aXe 也可以使用`npm`安装，并且可以与任务运行器（如Grunt 和Gulp），自动化框架（如Selenium和Cucumber），单元测试框架（如Jasmin）集成，以及更多其他功能（详见main aXe page ）。

### 屏幕阅读器

为了了解有严重视力障碍的人是如何浏览网页的，我们需要测试屏幕阅读器。有几款屏幕阅读器：

* 有些是付费产品，比如JAWS (Windows) 和Window Eyes (Windows).
* 有些是免费产品，比如NVDA (Windows), ChromeVox (Chrome, Windows, and Mac OS X), 和Orca (Linux).
* 有些内置在操作系统中，比如VoiceOver (Mac OS X and iOS), ChromeVox (on Chromebooks), 和TalkBack (Android).

通常，屏幕阅读器是独立运行的应用程序，并且不仅仅支持阅读网页，也支持阅读其他应用程序。也有例外（比如 ChromeVox 是一个浏览器扩展程序）。不同的屏幕阅读器可能在控制键和表现上稍有不同，所以你必须查阅你选择的屏幕阅读器的文档来获取相关细节。总体来说他们是大同小异的。

一起看一下我们对几款不同的屏幕阅读器的测试。这将帮助你大致了解它们如何工作以及如何测试它们。

**备注：** WebAIM 的 Designing for Screen Reader Compatibility提供了一些关于屏幕阅读器的使用和如何开发应用以最好的适用屏幕阅读器的信息。你也可以看下Screen Reader User Survey #6 Results这篇有关屏幕阅读器一些有趣的统计信息的文章。

#### VoiceOver

VoiceOver (VO) 是 Mac/iPhone/iPad 上的免费应用，所以如果你使用苹果公司的产品，可以用 VO 来进行测试。我们在 Mac OS X 系统上测试了它。

按下 Cmd + Fn + F5 打开它。如果你之前没用过 VO，将会出现一个可以选择是否开启 VO 的欢迎界面，并且还会有教程指导你如何使用。再次按下 Cmd + Fn + F5 可以关闭。

**备注：** 你应该至少看一遍教程，它对于你了解 VO 是非常有用的。

当 VO 开启时，你会看到一个会显示当前选中信息的黑色框在屏幕的左下角，除此之外屏幕显示大体还是相同的。当前选中的部分也会出现一个黑色的边框以进行高亮显示，这个黑色框就是 VO 的光标。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/Accessibility/voiceover.png)

在使用中，你会用到"VO 修饰键"，"VO 修饰键"是一个单独键或组合键，当你使用 VO 的快捷键时，你需要额外按下这个"VO 修饰键"。屏幕阅读器通常都会有修饰键，防止它们的快捷键和其他程序的快捷键冲突。VO 的修饰键是 CapsLock, 或 Ctrl + Option。

VO 有很多快捷键，我们没有全部列出来。只把测试网页无障碍常用的一些在下面列出了。表格里，"VO"代表"VO 修饰键"。

| 快捷键 | 描述 |
| --- | --- |
| VO + 方向键 | 移动 VO 光标 |
| VO + 空格键 | 选择/激活高亮的部分，包括 Rotor 里的内容（关于 Rotor 见下面） |
| VO + Shift + 下 | 移动到组合项目里（比如 HTML 表格，或表单等）。进入组合里，你能使用下面的快捷键。 |
| VO + Shift + 上 | 离开组合项目 |
| VO + C | (当在表格里面时) 阅读当前列的头部 |
| VO + R | (当在表格里面时) 阅读当前行的头部 |
| VO + C + C | (当在表格里面时) 阅读当前列，包括列头 |
| VO + R + R | (当在表格里面时) 阅读当前行，包括对应于每个小行的头 |
| VO + 左，VO + 右 | (当在水平的选项卡里面时，比如日期选择或时间选择) 切换选项 |
| VO + 上，VO + 下 | (当在垂直的选项卡里面时，比如日期选择或时间选择) 切换选项 |
| VO + U | 使用 Rotor。Rotor 使用列表展示标题，链接，表单选项等，以便为我们提供便利的导航。 |
| VO + 左，VO + 右 | (当在 Rotor 里) 切换到其他列表 |
| VO + 上，VO + 下 | (当在 Rotor 里) 在当前列表里，切换到其他项 |
| Esc | (当在 Rotor 里) 推出 Rotor |
| Ctrl | (当 VO 阅读时) 暂停/继续 |
| VO + Z | 重复上一句话 |
| VO + D | 进入 Mac 的程序坞，你能选择运行哪个应用 |

看起来很多，但当你用起来时还好，因为 VO 通常会给你提醒，在哪里应该用哪个快捷键。现在试试运行 VO，在屏幕阅读器测试屏幕阅读器测试章节做个测试吧。

#### NVDA

NVDA 只能运行在 Window 系统，你需要安装它。

1. 在nvaccess.org下载。你能选择免费下载，或赞助后再下载；你需要在下载前提供你的邮箱地址。
2. 下载完成后，开始安装 - 双击安装程序，接受条款，一步步按提示来。
3. 双击 NVDA 程序或快捷方式，或者按下 Ctrl + Alt + N 打开它。你会看见欢迎界面。你能选择一些选项，然后按下 OK 继续。

NVDA 现在在你的电脑上开启了。

在使用中，你会用到"NVDA 修饰键"，"NVDA 修饰键"是一个单独键，当你使用 NVDA 的快捷键时，你需要额外按下这个"NVDA 修饰键"。屏幕阅读器通常都会有修饰键，防止它们的快捷键和其他程序的快捷键冲突。NVDA 的修饰键是 Insert 键（默认）, 或 CapsLock 键（在欢迎界面可以选择使用该键）。

**备注：** 关于高亮方面，NVDA 比 VoiceOver 做的更好。当滚动过标题、列表等元素时，你选中的项目会被一个细微的边框包住以高亮，但不是对于所有元素都是这样的。如果你感觉迷失方向了，可以按 Ctrl + F5 刷新页面，并从顶部重新开始。

NVDA 有很多快捷键，我们没有全部列出来。只把测试网页无障碍常用的一些在下面列出了。表格里，"NVDA"代表"NVDA 修饰键"。

| 快捷键 | 描述 |
| --- | --- |
| NVDA + Q | 关闭 NVDA |
| NVDA + 上 | 阅读当前行 |
| NVDA + 下 | 在当前位置开始阅读 |
| 上和下，或者 Shift + Tab 和 Tab | 移动到上/下一项，开始阅读 |
| 左和右 | 移动到当前项的上/下一个字符，开始阅读 |
| Shift + H 和 H | 移动到上/下一标题，开始阅读 |
| Shift + K 和 K | 移动到上/下一链接项，开始阅读 |
| Shift + D 和 D | 移动到上/下一文档界标（比如<nav>），开始阅读 |
| Shift + 1–6 和 1–6 | 移动到上/下一标题（标题 1 - 6），开始阅读 |
| Shift + F 和 F | 移动到上/下一表单选项，聚焦 |
| Shift + T 和 T | 移动到上/下一数据表，聚焦 |
| Shift + B 和 B | 移动到上/下一按钮，阅读它的 label |
| Shift + L 和 L | 移动到上/下一列表，阅读它的第一项 |
| Shift + I 和 I | 移动到上/下一列表，开始阅读 |
| Enter/Return | (当链接或按钮或其他可激活的项选中时) 激活项 |
| NVDA + 空格 | (当选中表单时) 进入表单，或如果已经在表单里的情况下，离开表单 |
| Shift Tab 和 Tab | (当在表单里面时) 切换到下一个 input |
| 上 和 下 | (当在表单里面时) 改变 input 的值 (例如选择框). |
| 空格 | (当在表单里面时) 选择已选中的值 |
| Ctrl + Alt + 方向键 | (当选中表格时) 切换表格单元格 |

#### 屏幕阅读器测试

现在你学会了如何使用屏幕阅读器，来使用它做一些无障碍测试吧。这样你才能了解屏幕阅读器在好的网页和坏的网页之间不同的表现：

* 浏览good-semantics.html, 留意屏幕阅读器如何找到标题并将其用于导航。再看一下bad-semantics.html, 留意屏幕阅读器不会得到这些信息。想象一下，当尝试浏览非常长的文本页面时，这会是多么使人烦躁。
* 浏览good-links.html, 并留意当不在上下文时他们对于你理解内容的帮助。而bad-links.html 就无法帮助你理解— 他们仅仅会提示"click here（点击这里）".
* 浏览good-form.html, 并注意如何使用其标签描述表单输入，因为我们已经正确使用了<label>。在bad-form.html, 只能获取到无用的空白标签。
* 浏览punk-bands-complete.html, 并查看屏幕阅读器如何将内容的列和行关联起来，并一起读取它们，因为我们已经正确定义了标头。在bad-table.html, 无法得知单元格的关联关系。请注意，当页面上只有一个表时，NVDA 似乎表现得有些杂乱无章。你可以改用WebAIM's table test page进行测试。
* 看一下之前看过的WAI-ARIA live regions example, 并注意屏幕阅读器将实时读取不断更新的部分。
### 用户测试

如上所述，你不能仅依靠自动化工具来确定网站上的无障碍问题。建议在制定测试计划时，包含一些用户测试无障碍的计划 (有关更多内容，请参阅前面的用户测试部分)。尝试让一些屏幕阅读器用户，一些全键盘用户，一些有听觉障碍的用户或其他用户参与测试，以满足你的需求。

测试无障碍检查清单
---------

以下列表提供了一个清单，供参考，以确保已对项目执行建议的无障碍测试：

1. 确保 HTML 尽可能是语义化的。验证是一个好方法，就像使用审计工具。
2. 检查当关闭 CSS 时，你的内容能够被理解。
3. 确认功能是全键盘可访问的。使用 Tab 键、回车键等做测试。
4. 确保非文本内容有替代文本。 审计工具能够很好地发现问题。
5. 确保颜色和颜色对比度是可接受的，使用合适的工具测试。
6. 确保隐藏的内容可以被屏幕阅读器读取。
7. 尽可能的使功能在没有 JavaScript 的情况下也可以正常使用。
8. 在合适的地方使用 ARIA 来提供无障碍。
9. 使用审计工具测试一下你的网站。
10. 使用屏幕阅读器实际测试一下。
11. 在你的网站上可以添加无障碍策略/声明，以说明你的为无障碍做了什么。
寻找帮助
----

无障碍还会遇到许多其他问题。你要学会如何在线查找答案。请查阅 HTML 和 CSS 文章的“寻找帮助”部分，以获取一些指导。

总结
--

希望本文能让你了解无障碍，以及帮助你解决遇到的一些无障碍问题。

下一篇文章，我们将详细介绍特征检测。

* 上一页
* Overview: Cross browser testing
* 下一页


处理常见的 HTML 和 CSS 问题 - 学习 Web 开发

处理常见的 HTML 和 CSS 问题
===================

* 上一页
* Overview: Cross browser testing
* 下一页

通过之前的准备，我们现在将专门研究在 HTML 和 CSS 代码中遇到的常见跨浏览器问题，以及哪些工具可以用来防止问题的发生，或者解决出现的问题。这包括静态分析代码，处理 CSS 前缀，使用浏览器开发工具来跟踪问题，使用 polyfills 添加到浏览器的支持，解决响应的设计问题，等等。

|  |  |
| --- | --- |
| 前提： |
 熟悉核心的 HTML，CSS
 和 JavaScript 语言；了解跨浏览器测试中涉及的高级概念。
  |
| 本文目标： |
 为了能够诊断常见的 HTML 和 CSS
 跨浏览器问题，并使用适当的工具和技术来解决它们
  |

HTML 和 CSS 的问题
--------------

实际上，HTML 和 CSS 的问题在于，它们相当简单，简单到开发人员通常不会认真对待它们，因此难以确保代码简洁、高效，或是能在语义上描述页面各部分的功能。在最坏的情况下，一些网站甚至选择使用 JavaScript 来生成整个网页的内容和样式，这使得页面的可读性较差，并且性能低下（对性能而言，生成大量 DOM 元素的操作是较为昂贵的）。

**备注：** 关于使用 JavaScript 生成网页的选择，抛开一切非决定性的因素，最终的关键还是我们开发者。
 非决定性因素，比如页面本身，举个例子，如果页面是由大量的重复或有规律片段组成，使用适当、理想的 JavaScript 来生成这样的页面会是不错的选择。

 但是，如果我们的决定性因素，开发者，不了解或不重视有关的问题，甚至盲目地崇拜一些基于 JavaScript 的前端页面生成方案，可能一切都是白搭。
 反过来，如果开发者深谙简化、语义和性能等的意义与价值，对不同的方案有全面的了解，即使使用 JavaScript 在前端生成网页，最终也可以产生各方面表现良好的页面。虽然，多数情况下，这样的开发者往往会保留页面的基本框架，不由 JS 生成。

在其他情况中，不同浏览器对新特性的支持不同，也可能会使某些功能和样式对某些用户不起作用。除此之外，响应式设计问题也很常见——在桌面浏览器中看起来不错的网站可能会在移动设备上给出可怕的体验，因为内容太小而无法阅读，或者性能被代价昂贵的动画严重拖累。

让我们前进吧，看看我们如何能够减少 HTML / CSS 中的跨浏览器错误。

首先：解决一般问题
---------

我们在本系列的第一篇文章中说过，一个好的策略是在桌面/移动设备上测试几个现代的浏览器，以确保你的代码正常工作，然后才能专注于跨浏览器问题。

在我们的调试 HTML 和调试 CSS 两篇文章中，我们也提供了一些关于调试 HTML/CSS 的基本指导——如果你不熟悉基础知识，那么在继续之前，你得学习这些文章。

基本上，这是一个如何检查你的 HTML 和 CSS 代码，是否格式正确、不包含任何语法错误的问题。

**备注：** CSS 和 HTML 之间的一个常见问题，就是不同的 CSS 规则之间发生冲突。当你使用第三方代码时，问题可能大到难以修补。例如，你可能正用着一个 CSS 框架，并发现它使用的一个类名与你已经用于某处的类名冲突。或者，你可能会发现，由某种第三方 API（例如生成广告横幅的脚本）生成的 HTML 包含你已经用于别处的类名称或者 ID。
 为了确保不会发生这种情况，你需要首先研究一下你正在使用的工具，了解它们可能使用的类名与 ID，并围绕，或者规避着它们来设计你的代码。设法为 CSS 加上“命名空间”也是值得的，例如，如果有一个小部件，就先确保它有一个独特的类，然后基于这个类来选择元件内的这个类的选择器，这样一来，冲突的可能性就小下来了。例如 .audio-player ul a。

### 验证

对于 HTML，要确保所有的标签都被正确地关闭和嵌套，写好 DOCTYPE，而且正确地使用各种标签。一个好的策略是定期验证你的代码。一个可以做到这一点的服务是 W3C 标记验证服务，提供你的代码，它就能返回错误列表：

![The HTML validator homepage](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/validator.png)

CSS 有一个类似的路线——你需要检查你的属性名拼写是否正确，属性值拼写正确，并且对于它们使用的属性是有效的，你不会丢失任何大括号，等等。为了这个目的，W3C 也有一个 CSS 验证器。

### Linter

另一个很好的选择就是所谓的 Linter 程序，它不仅可以指出错误，还可以标记关于 CSS 中不良做法的警告，以及其他一些要点。大家可以定制 Linter，以得到更严格或者更宽松的错误/警告报告。

有许多在线 Linter 程序，其中最好的可能是“脏标记”Dirty Markup（HTML，CSS，JavaScript）和 CSS Lint（仅限 CSS）。它们可以让你把代码粘贴到一个窗口中，并且会用十字标记任何错误，然后可以将它们悬停，以获得错误信息，通知你问题是什么。肮脏的标记还允许你使用清理按钮修复你的标记。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/dirty-markup.png)

但是，将代码复制并粘贴到网页上以检查其有效性是不太方便的。你真正想要的 Linter 可以和你的工作流更加契合，减免一切窗口切换等麻烦。

许多代码编辑器都有 linter 插件。Github 的 Atom 代码编辑器例如有一个丰富的插件生态系统可用，有很多 linting 选项。向你展示一个这样的插件通常如何工作的例子：

1. 安装 Atom（如果你还没有安装最新版本），请从上面链接的 Atom 页面下载。
2. 转到 Atom 的\*首选项...\*对话框（例如在 Mac 上选择 *Atom > Preferences...* 或在 Windows / Linux 上选择 *File > Preferences...*），然后在左侧菜单中选择 Install 选项。
3. 在搜索包文本字段中，输入“lint”，然后按 Enter / 回车键搜索与 lint 相关的包。
4. 你应该在列表顶部看到一个名为 lint 的包。首先安装它（使用安装按钮），因为其他短裤依靠它工作。之后，安装用于 linting CSS 的 linter-csslint 插件，和用于 lint HTML 的 linter-tidy 插件。
5. 软件包完成安装后，尝试加载一个 HTML 文件和一个 CSS 文件：在行号旁边会看到用绿色（警告）和红色（错误）圆圈，突出显示任何存在的问题，底部则提供行号、错误消息，有时有建议的值或者其他修复。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/atom-htmltidy.png)
![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/atom-csslint.png)

其他受欢迎的编辑器也有类似的 linting 包可用。例如：

* SublimeLinter for Sublime Text
* Notepad++ linter
* VSCode linter
### 浏览器开发者工具

大多数浏览器中内置的开发人员工具还提供了有用的工具来查找错误，主要是针对 CSS 的。

**备注：** 在开发工具中，HTML 错误不会直接显示，因为浏览器会尝试自动纠正错误形成的标记；目前，W3C 验证器是获取 HTML 错误的最佳方法——请参阅上面的验证。

例如，在 Firefox 中，CSS 检查器将显示未应用的 CSS 声明，并带有警告三角形。悬停警告三角将提供一个描述性的错误信息：

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/css-message-devtools.png)

其他浏览器的开发者工具有相似的功能。

常见的跨浏览器问题
---------

现在让我们继续看看一些最常见的跨浏览器 HTML 和 CSS 问题。这里，我们重点先看的方面是一些浏览器缺乏对现代功能和布局的支持所造成的问题。

### 旧版浏览器不支持新特性

这是一个常见的问题，尤其是当你需要支持旧的浏览器（例如旧的 IE 版本），或者你正在使用使用 CSS 前缀实现的功能时。通常，大多数核心 HTML 和 CSS 功能（如基本的 HTML 元素，CSS 基本颜色和文本样式）在大多数浏览器中都可以使用，当你开始想要使用 Flexbox，HTML5 视频/音频 (en-US)，甚至更新的 CSS Grid 或 `-webkit-background-clip：text` 等新功能时，会发现更多的问题。

一旦你确定了你将要使用的潜在问题技术列表，研究它们所支持的浏览器以及相关技术是否有用是一个不错的主意。请参阅下面的查找帮助。

#### HTML 回退行为

一些问题可以通过利用 HTML / CSS 的自然工作方式来解决。

无法识别的 HTML 元素被浏览器视为匿名内联元素（有效内联元素，没有语义值，类似于 `<span>` 元素）。你还可以参考他们通过自己的名字，并用 CSS 样式它们，例如 - 你只需要确保它们表现为你想让他们，例如对所有新的语义元素设置 `display: block;`（如 `<article>`、`<aside>` 等），但现在，基本只有在旧版本的 IE 不能识别他们（IE 8 和更低）。这样，新的浏览器就可以正常使用这些代码，但是旧的 IE 版本也可以对这些元素进行样式化。

**备注：** 请参阅IE 条件注释以获取最佳实践。

更复杂的元素，如 HTML `<video>`、`<audio>`，和 `<canvas>`（和其他功能除外）有自然的 fallback 加入机制，其工作原理与上述相同。你可以在开始和结束标记之间添加回退内容，非支持的浏览器将有效地忽略外部元素并运行嵌套的内容。

例如：

```html
<video id="video" controls preload="metadata" poster="img/poster.jpg">
  <source src="video/tears-of-steel-battle-clip-medium.mp4" type="video/mp4" />
  <source
 src="video/tears-of-steel-battle-clip-medium.webm"
 type="video/webm" />
  <source src="video/tears-of-steel-battle-clip-medium.ogg" type="video/ogg" />
  <!-- Flash fallback -->
  <object
 type="application/x-shockwave-flash"
 data="flash-player.swf?videoUrl=video/tears-of-steel-battle-clip-medium.mp4"
 width="1024"
 height="576">
    <param
 name="movie"
 value="flash-player.swf?videoUrl=video/tears-of-steel-battle-clip-medium.mp4" />
    <param name="allowfullscreen" value="true" />
    <param name="wmode" value="transparent" />
    <param
 name="flashvars"
 value="controlbar=over&image=img/poster.jpg&file=flash-player.swf?videoUrl=video/tears-of-steel-battle-clip-medium.mp4" />
    <img
 alt="Tears of Steel poster image"
 src="img/poster.jpg"
 width="1024"
 height="428"
 title="No video playback possible, please download the video from the link below" />
  </object>
  <!-- Offer download -->
  <a href="video/tears-of-steel-battle-clip-medium.mp4">Download MP4</a>
</video>
```

这个例子（来自创建一个跨浏览器的视频播放器 (en-US)）不仅包括旧的 IE 版本的 Flash 视频后备，而且还包括一个简单的链接，允许你下载视频，这样，即使 Flash 播放器无法运作，用户仍然可以访问视频。

HTML5 表单元素也表现出了后备素质--HTML5 引入了一些特殊的 `<input>` 类型，用于将特定的信息输入到时间，日期，颜色，数字等形式中。这些非常有用，特别是在移动平台上，它提供了一种无痛苦的输入数据对用户体验非常重要。当使用这些输入类型时，支持平台提供特殊的 UI 小部件，例如用于输入日期的日历小部件。

以下示例显示日期和时间输入：

```html
<form>
  <div>
    <label for="date">Enter a date:</label>
    <input id="date" type="date" />
  </div>
  <div>
    <label for="time">Enter a time:</label>
    <input id="time" type="time" />
  </div>
</form>
```

此代码的输出如下所示：

**备注：** 你也可以在 GitHub 上以forms-test.html 的形式直接看到这个运行（参见源代码）。

如果你在支持的浏览器（如桌面/ Android Chrome 或 iOS Safari）上查看该示例，则会在尝试输入数据时看到特殊的小部件/功能。在一个非支持的平台上，例如 Firefox 或 Internet Explorer，输入只会回退到正常的文本输入，所以至少用户仍然可以输入一些信息。

注意：当然，这对于你的项目需求来说可能不是一个很好的解决方案 - 视觉呈现方面的差异并不是很大，而且更难保证数据将以你希望的格式输入。对于跨浏览器表单，依靠简单的表单元素，或者选择性地使用高级表单元素来支持浏览器，或者使用提供体面的跨浏览器表单小部件（如 jQuery UI 或 Bootstrap datepicker）的库可能会更好。

#### CSS 回退行为

CSS 的情况可以说比 HTML 更好一些。如果一个浏览器遇到一个它不明白的声明或规则，它只会完全跳过它，而不会强行应用它或者抛出错误。这可能会让你和你的用户感到沮丧，如果这样的错误滑入产品代码，但至少意味着整个网站不会因为一个错误而崩溃，如果巧妙地使用，你可以使用它。

让我们来看一个例子——一个简单的 CSS 风格的框，它有一些 CSS3 特性提供的样式：

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/blingy-button.png)

**备注：** 你也可以在 GitHub 上浏览这个例子：button-with-fallback.html 的形式运行（另见源代码）。

该按钮有一些样式声明，但我们最感兴趣的两个如下：

```css
button {
  ...

  background-color: #ff0000;
  background-color: rgba(255,0,0,1);
  box-shadow: inset 1px 1px 3px rgba(255,255,255,0.4),
              inset -1px -1px 3px rgba(0,0,0,0.4);
}

button:hover {
  background-color: rgba(255,0,0,0.5);
}

button:active {
  box-shadow: inset 1px 1px 3px rgba(0,0,0,0.4),
              inset -1px -1px 3px rgba(255,255,255,0.4);
}
```

在这里，我们提供了一个 RGBA background-color，改变悬停的不透明度，给用户一个暗示该按钮是交互式的提示，以及一些半透明的嵌入式 box-shadow 阴影，使按钮有一点纹理和深度。问题在于 RGBA 颜色和方块阴影在 9 以前的 IE 版本中不起作用 - 在较旧的版本中，背景根本不会显示出来，所以文本将是不可读的，这没有任何好处！

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/unreadable-button.png)

为了解决这个问题，我们添加了第二个 background-color 声明，它只是指定了一个十六进制颜色 - 这在旧版本的浏览器中得到了支持，并且在现代闪亮功能无法使用的情况下作为后备。访问此页面的浏览器首先应用第一个 background-color 值，当它到达第二个 background-color 声明时，如果它支持 RGBA 颜色，它将用这个值覆盖初始值。如果不是的话，它会忽略整个声明，继续前进。

**备注：** 这同样适用于其他 CSS 功能，如真正的媒体查询，@font-face 以及 @supports 块 - 如果不支持他们，浏览器只是忽略它们。

#### IE 条件注释

IE 条件注释是修改后的专有 HTML 注释语法，可用于将 HTML 代码有选择地应用于不同版本的 IE。这已被证明是一个非常有效的机制来解决跨浏览器的错误。语法如下所示：

```html
<!--[if lte IE 8]>
 <script src="ie-fix.js"></script>
 <link href="ie-fix.css" rel="stylesheet" type="text/css" />
<![endif]-->
```

只有当浏览器查看页面是 IE 8 或更旧时，此块才会应用 IE 特定的 CSS 和 JavaScript。lte 意思是“小于或等于”，但是也可以使用 lt、gt、gte、!(NOT) 以及其他逻辑语法。

**备注：** Sitepoint 的 Internet Explorer 条件注释提供了一个有用的初学者教程/参考，详细解释了条件注释语法。

正如你所看到的，这对于将代码修复应用于旧版本的 IE 尤其有用。我们前面提到的用例（使旧版本的 IE 可以使用现代语义元素）可以通过使用条件注释来轻松实现，例如，你可以在 IE 样式表中添加类似这样的内容：

```css
aside,
main,
article,
section,
nav,
figure,
figcaption {
  display: block;
}
```

然而，这并不是那么简单 - 你还需要通过 JavaScript 在 DOM 中创建你想要样式化的每个元素的副本，让它们变得可以风格化; 这是一个奇怪的怪癖，我们不会在这里让你知道细节。例如：

```js
const asideElem = document.createElement('aside');
 ...
```

这听起来像是一个痛苦的处理，但幸运的是有一个 polyfill 可以为你做必要的修复，除此之外 - 请参阅 HTML5Shiv 了解所有细节（请参阅手动安装以获取最简单的用法）。

#### 选择器支持

当然，如果你不使用正确的选择器来选择你想要的样式的话，就没有任何 CSS 功能可以应用！如果你只是错误地写了一个选择器，所以在任何浏览器中的样式都不像预期的那样，你只需要排除故障并找出选择器出了什么问题。我们发现使用浏览器的开发工具来检查你试图设计的元素是有帮助的，然后看一下 DOM 检查器倾向于提供的 DOM 树面包屑跟踪，看看你的选择器是否比它有意义。

例如，在 Firefox 开发工具中，你可以在 DOM 检查器的底部获得这种输出：

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/dom-breadcrumb-trail.png)

例如，如果你尝试使用此选择器，则可以看到它不会根据需要选择输入元素：

```css
form > #date
```

（date 表单输入不是直接在里面<form>;你最好使用一般的后代选择器而不是子选择器）。

然而，出现在 IE 9 比旧的版本的另一个问题是，没有任何新的选择（主要是伪类和伪元素如 :nth-of-type，:not，::selection，等）的工作。如果你想在你的 CSS 中使用这些，并且需要支持较老的 IE 版本，那么一个好的方法就是使用 Keith Clark 的 Selectivizr 库 - 这是一个小型的 JavaScript 库，可以在现有的 JavaScript 库（如 jQuery 或 MooTools）上工作。

1. 要尝试此示例，请创建 selectivizr-example-start.html 的本地副本。如果你看看这个现场直播，你会发现它包含两个段落，其中一个是风格。我们已经选择了这个段落 `p:first-child`，这在老版本的 IE 中不起作用。
2. 现在下载 MooTools和Selectivizr，并将它们保存在与示例 HTML 相同的目录中。
3. 将下面的代码放到 HTML 文档的开头，就在开始 `<style>` 标记之前：

```html
<script type="text/javascript" src="MooTools-Core-1.6.0.js"></script>
<!--[if (gte IE 6)&(lte IE 8)]>
 <script type="text/javascript" src="selectivizr-min.js"></script>
<![endif]-->
```

如果你尝试在旧版本的 IE 中运行它，它应该可以正常工作。

![](/zh-CN/docs/Learn/Tools_and_testing/Cross_browser_testing/HTML_and_CSS/new-selector-ie7.png)

#### 处理 CSS 前缀

CSS 前缀带来的另外一个问题是 - 这些是一种用于允许浏览器供应商在技术处于试验状态时实现自己版本的 CSS（或 JavaScript）特性的机制，所以他们可以使用它来获取它没有与其他浏览器的实现冲突，或者最终的前置实现。举个例子：

* Mozilla 使用 `-moz-`
* Chrome / Opera / Safari 使用 `-webkit-`
* 微软使用 `-ms-`

这里有一些例子：

```css
-webkit-transform: rotate(90deg);

background-image: -moz-linear-gradient(left, green, yellow);
background-image: -webkit-gradient(
  linear,
  left center,
  right center,
  from(green),
  to(yellow)
);
background-image: linear-gradient(to right, green, yellow);
```

第一行显示了一个transform带有-webkit-前缀的属性 - 这是在 Chrome 中使用变换功能所必需的，直到功能定稿，并且这些浏览器添加了该属性的前缀无版本（在撰写本文时，Chrome 支持这两个版本）。

最后三行显示了三个不同版本的linear-gradient() (en-US)函数，用于在元素的背景中生成线性渐变：

1. 第一个有一个-moz-前缀，并显示一个略老的语法版本（Firefox）
2. 第二个有一个-webkit-前缀，并显示一个更老的专有版本的语法（这实际上是从一个真正的旧版本的 WebKit 引擎）。
3. 第三个没有前缀，并显示语法的最终版本（包含在定义此功能的CSS 图像值和替换内容模块级别 3 规范中）。

前缀功能从来不应该用于生产网站——它们可能会在没有警告的情况下更改或删除，并导致跨浏览器问题。当开发人员决定只使用-webkit- 某个属性的版本时，这是一个特别的问题 - 这意味着该网站在其他浏览器中不起作用。这实际上发生了很多，其他浏览器已经开始实现-webkit-各种 CSS 属性的前缀版本，所以他们将使用这样的代码。由于这些类型的问题，浏览器供应商使用前缀最近已经下降，但仍然有一些需要注意。

如果你坚持使用前缀功能，请确保使用正确的功能。你可以在 MDN 参考页面上查找哪些浏览器需要前缀，以及像caniuse.com这样的站点。如果你不确定，你也可以直接在浏览器中做一些测试。

试试这个简单的例子：

1. 打开 google.com 或具有突出标题或其他块级元素的其他站点。
2. 右键/ Cmd +单击有问题的元素并选择 Inspect / Inspect 元素（或者浏览器中的任何选项） - 这应该在浏览器中打开开发工具，在 DOM 检查器中高亮显示该元素。
3. 寻找可以用来选择该元素的功能。例如，在撰写本文时，主要的 Google 徽标的 ID 为 hplogo。
4. 将对此元素的引用存储在变量中，例如：

```js
const test = document.getElementById("hplogo");
```

5. 现在尝试为你感兴趣的 CSS 属性设置一个新的值，你可以使用元素的style属性来做到这一点，例如尝试在 JavaScript 控制台中输入这些：

```js
test.style.transform = "rotate(90deg)";
test.style.webkitTransform = "rotate(90deg)";
```

当你开始在第二个点之后键入属性名称表示形式（请注意，在 JavaScript 中，CSS 属性名称是以较低的驼峰大小写，而不是连字符），JavaScript 控制台应该开始自动填充浏览器中存在的属性的名称并匹配到目前为止所写的内容。这对于找出在该浏览器中实现哪个版本的属性很有用。

在撰写本文时，Firefox 和 Chrome 都实现了 `-webkit-` 前缀和非前缀的transform！

一旦你找到了你需要支持的前缀，你应该把它们写在你的 CSS 中，例如：

```css
-ms-transform: rotate(90deg);
-webkit-transform: rotate(90deg);
transform: rotate(90deg);
```

这可以确保所有支持上述任何属性的浏览器都可以使该功能正常工作。值得把非前缀版本放在最后，因为那将是最新的版本，如果可能的话，你会希望浏览器使用它。例如，如果浏览器实现了-webkit-版本和非前缀版本，则它将首先应用-webkit-版本，然后用非前缀版本覆盖它。你希望这样发生，而不是相反。

当然，这样做很多不同的 CSS 规则可能会变得非常乏味。最好使用自动化工具来为你做。而这样的工具存在：

无前缀的 JavaScript 库可以附加到页面上，并自动检测浏览器查看页面的功能，并根据需要添加前缀。使用起来非常简单方便，虽然它有一些缺点（请参阅上面的链接了解详细信息），但有一点是可争议的，即在你的站点中解析每个样式表并在运行时添加前缀可能会消耗计算机的处理能力为一个大的网站。

另一个解决方案是在开发过程中自动添加前缀，而这个（和其他的东西除外）可以使用像Autoprefixer和PostCSS这样的工具完成。这些工具可以以多种方式使用，例如 Autoprefixer 有一个在线版本，允许你在左侧输入非前缀 CSS，并在右侧给出前缀添加版本。你可以使用Autoprefixer 选项中列出的符号来选择要确保支持的浏览器; 另请参阅基于此的Browserslist 查询以获取更多详细信息。例如，以下查询将选择所有主流浏览器的最后 2 个版本和 IE9 以上的版本。
```

last 2 versions, ie > 9
```

Autoprefixer 也可用于其他更便捷的方式 - 请参阅Autoprefixer 使用情况。例如，你可以使用任务运行器/构建工具（如Gulp或Webpack）在开发完成后自动添加前缀。（解释这些工作如何超出本文的范围。）

你也可以使用插件来编辑文本编辑器，如 Atom 或 Sublime 文本。例如，在 Atom 中：

1. 你可以通过转到首选项>安装，搜索 Autoprefixer，然后安装。
2. 你可以通过按“Autoprefixer 设置”按钮并在页面的“设置”部分的文本字段中输入查询来设置浏览器查询。
3. 在你的代码中，你可以选择要添加前缀的 CSS 部分，打开命令调色板（Cmd / Ctrl + Shift + P），然后输入 Autoprefixer 并选择 Autoprefixer 自动完成的结果。

作为一个例子，我们输入了下面的代码：

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

我们突出显示了它，并运行 Autoprefixer 命令，并用以下代码替换它：

```css
body {
  display: -webkit-box;
  display: -ms-flexbox;
  display: flex;
}
```

### 布局问题

另一个可能出现的问题是浏览器之间布局的差异。从历史上看，这往往是一个更大的问题，但是最近，现代浏览器倾向于更一致地支持 CSS，布局问题往往更倾向于：

* 对现代布局功能缺乏（或不同）支持。
* 布局在移动浏览器中看起来不太好（即响应式设计问题）。

**备注：** 过去，Web 开发人员使用 reset CSS 删除应用于 HTML 的所有默认浏览器样式，然后将自己的样式应用于顶部的所有样式 - 这样做是为了使项目的样式更加一致，并减少可能的跨浏览器问题，特别是像布局的东西。然而现在它被认为过头了。我们现在最好的等价物是normalize.css，这是一个整齐的 CSS，在默认的浏览器样式上略微增加一点，使事情更加一致，并解决一些布局问题。建议你将 normalize.css 应用于所有的 HTML 页面。

**备注：** 当试图追踪一个棘手的布局问题，一个好方法是添加一个明亮的颜色outline到有问题的元素，或附近的所有元素。这使得更容易看到一切放置在哪里。请参阅使用大纲可视化调试你的 CSS 以获取更多详细信息。

#### 支持新的布局特性

今天网络上的大部分布局工作都是使用浮动工具完成的 - 这是因为浮动支持得很好（可以回到 IE4，尽管如此，如果你尝试支持 IE，也需要调查一些错误很久以前）。但是，它们并不是真正用于布局的目的 - 使用浮动的方式实际上是一种黑客攻击 - 而且它们有一些严重的限制（例如，请参阅为什么选择 Flexbox？）

最近，出现了专门的布局机制，例如Flexbox和CSS Grids，这些布局机制使常见的布局任务变得更加容易并消除了这些缺点。但是，这些在浏览器中并没有得到很好的支持：

* CSS Grid 是非常新的；在撰写本文的时候，他们只在最新版本的现代浏览器中才得到支持。
* Flexbox 在现代浏览器中得到了很好的支持，但是在旧版浏览器中却存在问题。IE 9 根本不支持它，IE 10 和旧版本的 iOS /桌面 Safari 分别支持 Flexbox 规范的两种不兼容老版本。如果你想尝试在所有这些浏览器上使用 flexbox（请参阅高级跨浏览器 Flexbox以获得创意），则会导致一些有趣的浏览器前缀杂乱。

布局功能并不像简单的颜色，阴影或渐变那样容易提供优雅的后备。如果布局属性被忽略，你的整个设计可能会崩溃。因此，你需要使用功能检测来检测访问的浏览器是否支持这些布局功能，并根据结果有选择地应用不同的布局（我们将在后面的文章中详细介绍功能检测）。

例如，你可以将 Flexbox 布局应用于现代浏览器，然后将浮动布局应用于不支持 Flexbox 的旧浏览器。

**备注：** CSS 中还有一个相当新的特性 @supports，它允许你实现原生特征检测测试。

#### 响应式设计问题

响应式设计是创建网页布局以适应不同设备形式因素（例如不同的屏幕宽度，方向（纵向或横向）或分辨率）的做法。例如桌面布局在移动设备上看起来会很糟糕，所以你需要使用媒体查询来提供合适的移动布局，并确保使用视口正确应用它。你可以在响应式设计的构建模块中 (en-US)找到这些实践的详细说明。

解决方案也是一个大问题 - 例如，移动设备不太可能需要比台式电脑大的图像，并且更可能具有较慢的互联网连接，并且甚至可能使昂贵的数据计划浪费带宽成为更多的问题。另外，不同的设备可以具有一系列不同的分辨率，这意味着较小的图像可以出现像素化。有许多技术可以让你解决这些问题，从简单的移动第一媒体查询到更复杂的响应式图像技术。

可能出现问题的另一个困难是浏览器对使上述技术成为可能的特征的支持。媒体查询没有在 IE 8 或更少的支持，所以如果你想使用移动优先布局和具有桌面布局则适用于旧版本的 IE，你将有一个媒体查询应用于填充工具到你的网页，如 css3- mediaqueries-js 或 Respond.js。

寻找帮助
----

HTML 和 CSS 还有很多其他的问题。最重要的是如何在网上找到答案。

其中最好的支持信息来源是 Mozilla 开发者网络（你现在就在这！），stackoverflow.com 和 caniuse.com。

为使用 Mozilla 开发者网络（MDN），大多数人会搜索他们正在尝试查找信息的技术，以及术语“mdn”，例如“mdn HTML5 video”。MDN 包含几种有用的内容类型：

* 带有客户端网络技术浏览器支持信息的参考资料，例如<video> 参考页面。
* 其他支持的参考资料，例如 HTML 音频和视频元素支持的媒体格式 (en-US)。
* 解决特定问题的有用教程，例如创建跨浏览器的视频播放器 (en-US)。

caniuse.com 提供支持信息，以及一些有用的外部资源链接。例如，请参阅http://caniuse.com/#search=video（你只需在文本框中输入要搜索的功能）。

stackoverflow.com（SO）是一个论坛网站，你可以提出问题，并让开发人员分享他们的解决方案，查看以前的帖子，并帮助其他开发人员。在发布新问题之前，建议你先看看是否已经回答了你的问题。例如，我们在 SO 上搜索了“cross browser html5 video”，并且很快得到了具有完全跨浏览器兼容性的 HTML5 Video。

除此之外，请尝试使用你最喜爱的搜索引擎来解决你的问题。如果有的话，搜索特定的错误信息通常很有用 - 其他开发人员可能会遇到与你一样的问题。

总结
--

现在你应该熟悉 Web 开发中主要的跨浏览器 HTML 和 CSS 问题，以及如何解决这些问题。

* 上一页
* Overview: Cross browser testing
* 下一页