CSS-高流量网站高级教程-全-

CSS 高流量网站高级教程(全)

原文:Pro CSS for High Traffic Websites

协议:CC BY-NC-SA 4.0

零、前言

可以有把握地假设——因为你手里拿着这本书——你在一家规模合理的公司工作。这意味着你要处理官僚主义和繁文缛节,你要和许多不同层次的员工打交道。对您的软件或网站进行更改不会立竿见影,而且会受到传统协议和流程的拖累。如果这些都不符合你的情况,那么你是幸运的,因为你是少数。

这里提到的所有项目听起来都像是负面属性,但其中许多是不可避免的结果——尤其是在经历了快速增长的企业中。公司规模越大,通常对变化的反应就越慢。Antony 回忆说,他花了几个月的时间为一家大型蓝筹公司做一个项目,却发现(在项目完成后参观办公室时)它在底层有自己的软件部门,完全有能力承担他刚刚完成的项目。这在大型企业中很常见;他们变得如此庞大,以至于很难在全体员工之间有效地沟通工作。通常不同的团队发现他们一直在做同一件事情,而他们只是没有办法知道。

公司可以由许多因素驱动。它们可以是财务驱动、声誉驱动、品牌驱动或设计驱动,仅举几例。业务驱动因素定义了业务实践和协议。或许除了品牌或设计驱动的公司,我们开发人员从一开始就没有考虑到软件的视觉支柱。

在为这些公司开发网站的团队中,CSS 开发人员经常被忽视。许多人认为 CSS 是一件容易学习的事情,但它经常被交给服务器端开发人员,而不是 web 开发专家,围绕 CSS 的框架和静态文件经常在最后一刻被考虑,而不是从一开始就作为基础设施的一部分。尽管 CSS 的语法很简单,但其实现的含义并不简单。一个好的 CSS 开发人员在跨浏览器陷阱和如何避免它们,语义,可访问性,搜索引擎优化,以及由于糟糕的 CSS 构建和文档而产生的问题方面有经验。一个构建良好的 CSS 框架会对网站的性能产生巨大的影响。此外,对于许多开发人员来说,一个记录良好的 CSS 框架很容易以一致的方式进行工作。特别是在一个网站的生命之初,从一开始就做好这件事最终会有回报。

我们并不自称能够通过出色的沟通和快乐的员工使您的业务快速响应。然而,在这本书里,我们的目标是给你一个良好的基础,让你可以遵循一些流程,以确保 CSS 不会成为网站开发或性能的瓶颈。在最后一章和附录中,我们将给出我们所涉及的所有内容的实例,你可以在本书的网站([www.procssforhightrafficwebsites.com](http://www.procssforhightrafficwebsites.com))上看到代码示例。

背景信息

这本书假定读者掌握了某些知识。本节旨在确保你了解这些信息,并解释你能从本书中得到什么。经验丰富的开发人员或任何已经在 web 行业工作了一段时间的人应该能够安全地跳到第一章。

这本书是给谁的?

虽然我们将提供 CSS 代码的例子来演示我们所涉及的技术和主题,但这些并不是为了演示最新的选择器或属性,而是为了演示如何对代码进行格式化、注释和建模,以保持代码的合理性,并遵循流程中的最佳实践。为此,这本书不仅是关于开发人员的,也是关于整个团队的,应该对所有成员都有价值。

本书适用于以下内容:

  • 任何在高流量网站工作的人。这是任何网站,预计每天有超过 10,000 的独立访问者,或者偶尔有超过这个数量的流量高峰。
  • 任何在一个非常大的网站上工作的人,这个网站可能有超过 2000 个页面或者超过 30 个小型站点。
  • 任何在有大量员工修改相同代码库的公司中从事网站工作的人,有超过 30 名开发人员从事 CSS 工作。
  • 任何为有能力成为一个非常大的公司的公司工作并且想要为它的网络开发过程建立一个好的基础的人。
  • 没有大型团队工作经验的开发人员。

然而,所涉及的实践是任何规模的网站的最佳实践。

我会学到什么?

在本书的整个过程中,您将学到以下内容:

  • Value of process
  • How to share knowledge among employees and teams
  • How to get new CSS developers to work quickly?
  • How to integrate CSS into construction and deployment
  • How to write reusable modular CSS
  • How to maximize the performance of the website?
  • How to maintain brand consistency
  • Best practices for cross-browser and accessible CSS
  • Dynamic CSS

最后一章提供了一个简单的 CSS 框架,它是我们专门为这本书开发的,展示了我们接触到的许多东西,以及我们构建它所遵循的过程。四个附录提供了指南和流程的具体示例,您应该会觉得很有用。

为什么这本书和其他的不一样?

在开始写这本书之前,我们做了大量的调查,了解哪些替代资源是可用的。大量关于学习 CSS 基础知识、高级 CSS 技术、CSS3 选择器/属性以及不同 CSS 设计模式的书籍都很容易获得。

本书不与这些书名竞争,也不取代它们。相反,这本书探讨了在大型团队或大量个人团队中工作的挑战,在有许多页面或子网站接收大量流量的网站上工作。这本书不是关于使用最新最炫的技术进行图像替换或跨浏览器圆角;相反,它的重点是让团队的新成员更容易理解并添加到现有的代码中,并让您的基础设施中的 CSS 从一开始就被考虑,并以一种理智和高性能的方式构建。

尽管这本书既面向初学者,也面向专家,但我们假设您已经熟练使用 HTML 和 CSS,或者至少熟悉它们的语法。我们将在本书的各个章节中讨论模块化、可重用代码的使用,这些代码既健壮又实用。

关注点分离

关注点分离是编写 CSS 时需要理解的一个重要概念。存在许多不同的应用程序和系统架构。它们的理由和好处远远超出了本书的范围;然而,有必要简单解释一下多层架构,因为其背后的逻辑很容易应用于 CSS 和浏览器应用程序模型。

多层架构方法是一种分离逻辑、数据和表示的设计。 1 这通常描述一个客户机-服务器系统,其中有一个客户机应用程序和一个服务器应用程序。大部分处理都发生在服务器上,客户端负责显示信息,并为用户提供操作信息的界面。多层方法适用于以下情况:

  • 客户端应用:演示
  • 应用:逻辑
  • 服务器:数据

1 模型视图控制器(MVC)就是这样的一个例子。

以这种方式将层分开,可以清楚地得到任何特定代码、功能或任务的所有权。它还保持了一切的可重用性和易维护性。

这是更传统的客户机-服务器应用程序通常的行为方式。这种方法显然适用于 web 浏览器、web 服务器和数据库,但是在 web 浏览器中运行的代码也可以这样分解。我们通常用三种“语言”编写前端代码:HTML、CSS 和 JavaScript。我们的代码有可能被混在一起,就像下面的例子一样:

<a id="clickableAnchor" href="#" style="color:blue;" onclick="alert('clicked!');">Click me</a>

从技术上来说,这没有错;文本将是蓝色的,单击它时 JavaScript 将会运行。然而,有几个原因说明这种代码是不好的实践:

  • 不同类型的代码之间没有明确的界限。当 CSS 开发人员想要改变文本的颜色时,他们不能在不修改页面的情况下这样做。当 JavaScript 开发人员想要更改警告框中的文本时,如果不修改页面,他们就无法这样做。
  • 这些代码不可能被重用。如果我们所有的代码都是这样写的,并且决定所有的锚文本都应该是红色的,我们就必须修改每一个有锚的地方的代码。
  • 页面上的代码越多,下载的文件就越大。在某种程度上,这是在重复前面的观点,因为可重用的外部代码只需要下载一次。然而,这对搜索引擎优化(SEO)也有影响。
  • 文档中的代码类型越少,阅读、解析和编辑就越容易。

更合适的解决方案如下:

在头脑中:

`

`

体内:

<a href="#" id="clickableAnchor">Click me</a>

这个解决方案最终将我们的三个层次分开;它创建了关注点分离。现在,这些文件可以很容易地在多个页面之间共享,并且可以缓存以获得更好的性能。我们的层在前端领域中的关系如下:

  • HTML :数据
  • CSS :演示
  • JavaScript :行为

让我们更详细地讨论一下这些。

数据

出于网站的目的,我们通常称之为内容,而不是数据。你会经常听到“内容为王”这句话,这是一个很好的建议。你的标记应该有语义和良好的结构;就其本身而言,它应该是一个有效的、合理有序的文档。这确保了机器和人类都有很好的机会理解文档内容和文字的意图。如果一段内容是一个标题,它应该被标记为标题(而不是被标记为,例如,用粗体设置的段落);如果它显然是一个条目列表,它也应该如此标记(相对于一系列 div 或一些其他不太相关的元素),等等。当机器理解我们的意图时,它们有能力做以下事情:

  • 理解一个页面是关于什么的,哪些部分更重要/不重要——这对搜索引擎和辅助设备尤其重要。
  • 在不影响可读性的情况下,以不同的方式显示页面(例如,通过使用用户样式表)。这也意味着你的网站可以在旧浏览器中优雅地降级,并以不妨碍理解的方式显示你的内容。
  • 允许以非线性方式访问数据,这同样对辅助设备特别有用。

陈述

这一层只关心页面的外观。任何表面的或纯粹的装饰放在这里。如果您要选择字体、布局页面或更改颜色,这是合适的图层。如果你的品牌需要小写的文本或者非常小的标题,我们可以在 CSS 中这样做,而不会破坏页面中“真实”内容的语义。

行为

这有时被称为逻辑、业务或业务逻辑层。当我们以非标准的方式与页面交互时——例如,拖放,或者跨 XML 的异步 JavaScript(AJAX)请求(一种从服务器获取信息而不请求全新页面的方法),JavaScript 负责这一部分。

前端开发充满了例外

尽管分层方法带来的好处是显而易见的,但它永远不会像我们刚才说的那样一目了然。表达式可以驻留在 CSS 文件中,进行计算(虽然我们不提倡)。JavaScript 可以负责表示(比如菜单的外观/动画)。有时 JavaScript 负责向页面添加新内容。还有很多其他的例子。然而,打算并尝试以这种方式分离各层给了我们一个很好的起点,并且以这种方式进行 web 开发通常可以产生新的和聪明的方法来避免内容的重复并保持高性能。

有了这些基本信息,我们就可以开始了。第一章将向你介绍过程的重要性。虽然严格来说它不是一个 CSS 主题,但它是构建项目的重要基础。

一、流程的价值

在这一章中,我们旨在关注你的团队和公司中的流程如何帮助而不是阻碍你的生产力。这些主题中的许多并不特定于 CSS(层叠样式表),但是要编写可扩展的、在许多团队或网站之间共享的 CSS,拥有可靠的流程和一致性是至关重要的。我们将讨论开发方法,如何确保一致的代码风格、源代码控制、工具和命名约定。因为我们的目的是给你有用的实际例子,所以我们将在本书的最后给出一个示例流程。

在本章中,我们将了解:

  • 团队及其组成部分
  • 扩大业务规模
  • 处理员工流失问题
  • 代码的一致性
  • 工具
  • 版本控制
  • 支持
  • 样机研究
  • 开发方法

团队

可以说,CSS 开发中最重要的元素是支持开发人员的团队。每个团队都是不同的,不仅仅是成员的个性或团队的规模,还有团队内部的纪律和技能。有些团队包括几个服务器端开发人员和一个前端开发人员,有些团队包括构建 web 解决方案所需的全部技能:设计人员、前端开发人员、服务器端开发人员、数据库专家、测试人员等等。

这种规模的团队有其组成部分之间快速沟通的好处,但也有规模的缺点;由于各种原因,较大的团队对需求的响应较慢。究其原因,研究者称之为“责任分散”这意味着在较大的团队中,成员假设其他人在任何给定的时间点做任何给定的任务,而不是自己承担。如果没有明确的指导方针,就很难确定谁或者应该对意外的问题或任务负责,所以即使这些问题被注意到了,它们也被遗忘了。在较小的团队中,除了交流更加直接和有效之外,更多的注意力放在每个人身上,成员将承担责任并更加积极主动。然而,这个问题可以通过良好的项目管理来解决。如果你所有的任务都有明确的责任范围,就没有混乱的空间。

images 注:描述写 CSS 的人有很多术语:前端开发人员、客户端开发人员、网页开发人员、网页设计人员只是少数。

公司内部的流程会根据团队的类型而发生巨大的变化。对于没有设计师的团队来说,如果反馈给团队的需求已经包括了完整的设计和规范,那么就没有多少空间和时间向设计师或信息架构师反馈任何问题,并且开发更加远离需求创建流程。如果团队不包括测试人员,并且用户测试流程在您的团队之外,他们发现的错误或问题会在您完成开发后反馈给您,这是修复它们的效率最低的时候。

谁写你的 CSS 也有很大的区别。如果它是由网页设计师写的(希望是为你正在开发的东西做设计的人),那么只要他们擅长他们所做的事情,他们就会考虑到诸如可访问性、可用性和模板化之类的东西。如果设计师和你的团队是分开的,你可能会发现他们提供的设计本质上并没有考虑这些事情。最糟糕的情况是,CSS 是由那些完全不擅长前端代码的人编写的,因为他们不擅长他们正在做的事情,这可能弊大于利。CSS 看似简单,很容易编写低效臃肿的代码,很快成为遗留代码。然而,公司的流程通常基于传统,不容易改变。

项目经理负责最大限度地利用团队并处理这些问题。流程中最重要的部分之一是定义职责范围。在组建团队之前,需要清楚地规划这些领域,这样你就知道团队中应该包括谁。一个简单的例子如下:

  • 项目经理:项目经理负责向业务代表他的团队(以及向团队代表业务),确保一切都被正确记录,促进会议,并确保他的团队成员知道他们应该做什么,并拥有他们需要做的一切(包括帮助消除任何阻碍他们做这件事的障碍)。就团队而言,责任到此为止;如果一个项目失败了,很可能是项目经理被追究责任。
  • 团队领导/技术领导:团队领导负责产品的技术交付。他们需要确保了解项目的总体方向以及用于解决技术挑战的方法,并与团队成员交流以发现任何潜在的风险或陷阱,并提出解决它们的方法。
  • 开发商:术语开发商是一个广义的术语。在这个例子中,开发人员是承担项目中实际任务的人(编写 CSS)。他负责给出记录任务所需的估计和细节,向团队领导反馈技术问题,向项目经理反馈后勤问题,并完成任务。

随着这些角色的明确定义,谁应该做什么就变得显而易见了。通过定期的会议,任何问题都应该被迅速地发现和处理。

我们没有提到设计师或测试人员,因为他们并不总是在开发项目的团队中,但是如果他们是你团队的成员,列出他们的职责也是很重要的。如果你的团队中有会写 HTML 和 CSS 的网页设计师,你可以把他们当成开发人员。

一旦你计划好了你的团队,你的公司就很容易开始为你的每个团队和每个项目使用这个模板。我们鼓励你继续逐个项目地考虑你的团队是如何组成的。虽然严格的流程是一件好事,但是如果你不偶尔改变它们,你可能永远不会发现更好的方法来运行你的开发。

图 1-1 显示了一个团队设置的示例层级图。

images

图 1-1。分层设计的一个例子

变得越来越大

一旦你有了自己的流程,并且团队的所有成员都了如指掌,那么就不会有什么惊喜了。然而,在较小的公司或团队中,严格的流程被认为不太重要。假设每个人都是有用的、努力工作的贡献者(在小公司,如果不是这样,他们会很快被发现),快速自发的会议和面对面的交谈可以促进沟通。因为员工之间的互动更加亲密,所以培训新员工是一项更加实际的任务。政策每天都在变化,尽管流程对于高效的绩效仍然是有用的,但事实上,一点混乱有助于创造力。

随着公司和团队变得越来越大,这不再是一种奢侈,而是一项繁琐的工作。试图在新员工入职时对他们进行培训是一项艰巨的任务,需要专门的空间和更多的员工。保持团队之间的沟通畅通变得更加困难和耗时。精心制作的 CSS 样式和口头约定的工作方法以及保持网站一致和整洁的方法开始在越来越多的团队之间迷失。如果你的团队越来越大,越来越多的时间被用于个人管理,甚至只是在会议上等待轮到你发言。如果你有场外资源,会议的效率会变得更低,因为它们从面对面的交谈变成了电话会议和电子邮件。

异地资源的优势显而易见,因为您只需使用(并付费!)当你需要它们的时候,它们通常非常便宜——但这是以个人交流的损失为代价的。

面对面交流的好处如下:

  • 手势
  • 面部表情
  • 身体语言
  • 白色书写板
  • 直接
  • 清楚

远程通信的缺点如下:

  • 技术困难
  • 失去意义和意图
  • 失焦
  • 优先权的丧失
  • 理解力差
  • 浪费时间和资源

注意:使用视频会议、屏幕共享和白板软件可以减轻一些担忧,但它们带来了新的技术困难,丧失了即时性和即时满足感。

如果您的团队成员在不同的国家,时区差异和潜在的语言障碍会进一步加剧这些问题。然而,在印度或爱沙尼亚这样的国家使用远程开发人员可以降低成本,这对企业来说太有吸引力了,不容忽视,这可能是有充分理由的。

很明显,在公司规模很小、日益壮大的时候,在大白板上贴上草草写好的便利贴,在房间里大声传达重要信息等以前可以接受的方法,不再是有效的管理手段。团队中的每个人(希望是公司)都接受并同意的可靠流程将有助于减轻这些担忧。如前所述,如果清楚每个人应该执行什么任务以及何时执行,误解就更难发生。

如果每个人的所作所为都以可追踪和一致的方式记录下来,任何遗留的误解都可以被发现和解决。更重要的是,在小公司中,团队中疏忽、无能或低效的成员很难不被注意到——编写与遗留代码不一致的 CSS,在没有尽职调查的情况下修改代码,或者在相关代码中造成 bug 和缺陷——而在大公司中,这些代价高昂的个人可以在雷达下潜伏数月。我们将在本章中介绍的流程可以更快地展现和暴露它们的活动(或缺少活动)。

在小公司里,每个人都有发言权。然而,在大公司里,这种声音可能会被人群淹没,或者根本无法传到需要听到它的人的耳朵里。伟大的想法和改进可能会被忽视,员工对一家他们无法留下任何印象或印记的公司投资较少。反馈会议、公司会议、直线经理和更少层级等简单流程有助于确保每位员工都有参与和贡献的机会。

如果这些流程能够从一开始就在公司的思维方式中根深蒂固,它们将更容易实现,并成为增长的良好基础。

员工流失率高

许多大公司的员工流动率都很高——人们来来去去更加频繁,带走了他们的知识和技能。前端开发者更是如此;他们在今天的市场上需求量很大,通常在短时间内作为承包商工作,因此可以在没有任何警告的情况下从一个角色跳到另一个角色。尽管公司创始人希望他们的员工会永远忠于他们,但事实是大多数人都会在某个时候离开并继续前进。对于小公司来说,在每个员工身上投入了大量的金钱,有时甚至是感情。每一名新员工(无论是替换已离职的员工还是填补因成长而变得必要的职位)都需要入职和培训。这些员工有助于塑造公司,选择合适的人是必须认真考虑的事情。在更大的公司里,这变得更加重要。大公司有自己的个性和特点,有更多的东西需要去适应。新员工几乎必须被公司的行为所同化。开始变得有效率需要相当长的时间,而要达到在你的环境中感到舒适的效率水平则需要更长的时间。更复杂的流程意味着需要设置更多的用户账户和密码、阅读更多的文档、学习更多的政策,以及寻找更大的空间。这意味着引导和培训你的员工对效率至关重要——毕竟,你仍然在员工非生产期支付他们工资。

images 提示:尽你所能让新员工尽快开始工作是很重要的。

你应该探索的另一件事是如何更有效地留住这些员工。每个员工都会给企业带来成本,但随着时间的推移,他们获得的知识会成倍增长,优秀团队成员的价值也是如此。首先,考虑你提供的薪资和福利待遇是否比你的竞争对手好。在许多研究中发现的证据表明,增加金钱奖励以提高绩效的简单概念只有在考虑体力劳动时才成立。在追求更多脑力活动的情况下,更重要的是让员工感受到挑战和价值。为此,给他们足够的报酬是很重要的,钱不是问题,但你不必支付高于市场的价格来获得忠诚的员工。提高个人工作幸福感的非金钱奖励也很有价值。灵活的工作时间、培训、休假时间,甚至每天早上提供一碗水果都有助于提高员工在工作场所的满意度。一个好的食堂不仅能让你的员工开心、吃饱——还能让他们离办公桌更近,从而更快地回到工作岗位!

另一件有助于留住员工的事情是对他们的贡献表示赞赏。颁奖典礼或其他形式的表彰、聚会和外出晚会表明了对他们对公司的承诺的赞赏,并进一步加强了团队的整体实力。

虽然你希望你的员工能满足你的截止日期和要求,尽可能多产,但你也不希望他们精疲力竭。确保您的截止日期尽可能切合实际(让员工参与评估流程,并根据之前的评估来确定评估的准确性),并确保您的员工对工作场所感到满意,这将有助于保持良好的职业道德和工作效率。

最后,你要确保你的员工对他们的工作感到自豪,并感到投入其中。尽最大努力让每个人都有发言权会有所帮助。一些公司,比如谷歌,给他们的员工提供开发自己产品的机会,如果他们开发出对整个公司有价值的东西,他们会给予支持和认可。谷歌的每个星期五都是“谷歌星期五”,开发者被鼓励尝试新技术。这种玩法导致了 Gmail、Google News、Orkut、Google logo 成为功能齐全的 Pacman 游戏,以及其他内部创新。在 Atlassian,每三个月,他们的员工被允许做一天他们喜欢的任何发展,只要他们之后向公司提交他们的结果。开发人员对他们的工作感到自豪,这一点在开发人员自由支配时选择工作的项目中表现出来。在此期间,对开发人员(而不是业务)来说重要的 bug 被修复,拼写错误被纠正,重要的重构发生,流程中的问题被隔离,现有业务问题的解决方案出现。结果是你有更好的软件,但更重要的是,你有更快乐和更自豪的人。

所有这些建议都同样适用于 CSS 开发人员和其他员工。然而,web 开发世界发展和变化很快,可能很难跟上。如果员工对自己的工作充满热情,他们会希望保持自己的技能与时俱进,如果公司希望他们快乐,就会帮助他们实现这一点。尽管较新的 CSS 技术可能由于不完整的浏览器支持而尚未成熟,但应该给 CSS 开发人员留出时间来阅读和熟悉这些技术。允许你的 web 开发人员有机会参加会议(甚至可能代表你的公司在会上发言),表明了对员工和他们所关心的技术的承诺。保管好图书馆的参考书(比如这本!)考虑订阅杂志也有帮助,给开发者时间阅读杂志、博客和其他在线文章也有帮助。允许他们通过公司博客在线展示自己的发现通常是他们会喜欢的,但这可能取决于你公司的公开立场和政策。

让员工开心应该是首要考虑的问题,这将有助于留住他们更长时间,但员工辞职应该是有计划的,而不是恐慌的原因。

一致性比质量更重要

雇佣新员工时,你应该努力雇佣高素质的员工。你希望人们了解自己的东西,并有东西与团队的其他成员分享。然而,特别是在编程和编码领域,不同的人有不同的写作风格。

代码的不一致是有代价的,无论是时间还是资源。尽管以特定的方式编写 CSS 选择器可能更高效、更聪明,但是由于潜在的高人员流失率,如果代码不寻常或者更难理解,那么在团队成员之间共享就变得很困难。自己写 CSS 框架是好事,只要不是太复杂拿不起来。你也应该考虑一些我们在第四章中谈到的已经可用的 CSS 框架。如果实现你自己的 CSS 框架的流程需要复杂的构建脚本并使调试变得困难,也许这个解决方案被过度设计了。我们并不是建议因为性能和效率的原因不应该使用构建脚本(你可以在第九章中读到),但是流程应该简单。CSS 开发人员通常工作速度很快,经常一分钟在他们的文本编辑器和浏览器之间切换很多次。运行代码所需的任何流程都应该以不引人注目的方式实现。一个阻碍开发的构建流程对开发人员效率的影响比许多人想象的要大得多。进入 CSS 调试模式应该很简单,所以如果 CSS 在进入生产时被缩小,CSS 开发人员可以看到他们编写的代码,而不是 CSS/DOM 检查器报告选择器在第 1 行,那里只有一行。

一个开发者囤积了你的代码的某些部分如何工作的所有知识,这应该是一个明显的危险信号。如果这个开发人员选择离开公司,或者由于某种原因不能再为他们工作了,就会有一个直接的知识赤字。新的开发人员可能在理解代码上有困难,一段停滞期开始了,同时这些开发人员努力掌握代码的复杂性。如果没有之前的 CSS 开发人员在那里指导他们,CSS 的大部分区域可能会被搁置多年,因为没有其他开发人员有足够的勇气删除它们,因为他们害怕在网站的其他地方引起问题。遗留代码的这种停滞是非常普遍的,并且很难解决,成本也很高。

为此,最好尽可能使用更简单、更清晰的代码,以便将知识囤积保持在最低水平。一些公司选择以特定的时间间隔频繁地轮换他们的开发人员到他们项目的不同部分来解决这个问题。事实上,尽管有明显的时间成本,这种方法有一些相当大的好处,因为每个团队成员对项目作为一个整体是如何工作的有一个很好的想法,并且可以考虑如果没有这种整体观点可能会被忽略的因素。在开发流程中发现的问题比在计划流程中发现的问题更难克服。这种方法的主要缺点是,开发人员不断地从一个难题转移到另一个难题,可能会发现很难“进入状态”,这可能会令人沮丧,并且会增加成本。

在这些场景中,一致性的价值不能被夸大。举一个非常基本的例子,一个人可以用这种方式编写他们的 CSS 代码:

`/* Main Heading */

mainHeading {

font-size: 2em;
   font-weight: bold;
   color: red;
}`

另一个人可能会这样写:

`/========================================================================
   Main Heading
========================================================================
/

main-heading {font-size:20px;font-weight:bold;color:#ff0000}`

这两种都是完全有效的,技术上正确的 CSS 编写方式。但是,如果这两个人在不同的时间处理同一个 CSS 文件,那么您可能会在同一个文档中混合使用这两种截然不同的编码风格。这导致文档难以扫描和阅读。许多开发人员在发现不同于他们自己的代码风格时,可能会以他们认为更好的方式重新格式化代码。最初的开发人员可能会以同样的方式将其格式化。这既没有效率,也没有必要。

CSS 比大多数编码语言更容易受到这个问题的困扰。将代码行放入一个文件中很容易,直到某个部分正确呈现,但是很难隔离出可以安全删除或不再使用的部分。为此,严格格式化您的 CSS 文档,并将选择器和规则保持在正确的位置,这对于避免代码中遗留下来的难以定位的部分非常重要。糟糕或仓促编写的代码被称为“技术债务”,这个术语是沃德·坎宁安在 1992 年创造的。只要你还钱(重构和修复代码),一点债务是没问题的,但是你等待的时间越长,利息就越多(构建在初始代码之上并依赖于初始代码的代码),直到你破产(不得不从头开始)。

为了解决这个问题,在指南中预定义标准是很重要的,这样每个人都知道 CSS 在你的组织中是以一种特殊的方式编写的。任何规模的大多数公司都有某种 wiki(见下文)或公司手册,这是存储此类指南的合适位置。尽管已经过时,BBC 还是在网上发布了 CSS 指南。

我们不建议制定严格执行的硬性指导方针(例如,您不应该使用!important、、 1 、,但在某些情况下,这可能是最合适的做法)。然而,我们建议存在某种形式的指导方针。以下是 CSS 格式指南的一部分示例:

  • CSS 的所有相关块都应该以这种格式的注释作为前缀,以帮助 CSS 文档的视觉扫描和可搜索性:/*========================================================================    Main Heading ========================================================================*/
  • 所有颜色都应该是十六进制格式(#123456)。
  • 所有的 CSS 选择器和规则应该在一行上,以节省空间,并在一个屏幕上同时显示更多的选择器。

这些要点作为一个例子,不一定是我们建议您采取的步骤。在这本书的最后,我们会给你一个 CSS 格式指南的例子。对于本指南,使用莫斯科(确定优先级的常用方法)很有用。莫斯科(在这种情况下)指的是:

  • 我必须这样做
    • 这条规则必须一直遵守,没有例外。
  • S 应该这样做
    • 除非有合理的理由,否则任何时候都应该遵守这条规则。
  • C 可以这样做
    • 这与其说是一条规则,不如说是一条技巧,可以在适当的时候使用。
  • 现在我不会这么做,但将来我会这么做(??)
    • 这是你现在不能做的事情(可能是因为缺乏浏览器支持),但将来可能会考虑。

使用这种方法很有用,因为这意味着所有 CSS 主题都被捕获,以证明它们已经被考虑过了,并且先前的结论可以保留下来供将来讨论。

同样重要的是,在可能的情况下,以及在不清楚的情况下,在我们的指南中解释这些规则的原因。

从商业的角度来看,创建干净的、写得好的 CSS 是很难证明的。这并不是说企业希望编写糟糕或混乱的代码,而是我们的工作通常基于业务需求。企业希望交付这些需求,并让团队准备好尽快转移到其他工作上。为此,你需要一个很好的妥协。你想要好的干净的代码——你知道它使未来的生活更容易,并且允许你为你的工作感到自豪。但是也许复杂而简洁的代码(并不总是好代码)弊大于利。如果你有一个坚实和严格的结构和方法,你坚持,我们的新员工可以适应我们的内部编码风格,最大限度地减少大惊小怪,直接进入生产。如果您知道哪些样式在哪些文件中,以及如何定位这些文件中的 CSS 块,您就可以立即找到需要修改的部分,而不需要无休止地争论(几乎没有商业价值)使用哪个位置。


1 !重要的在第三章中有更详细的描述。

当然,极端来说,过于冗长和注释过多的代码同样难以处理。选择器可能会变得过于具体,注释可能会占据我们所有的屏幕空间,使浏览文档变得困难。我们将在第二章中看到注释的用法。

这并不意味着使用新技术是一件坏事,或者你不应该争论技术。然而,这确实意味着,作为我们进程的一部分,这些辩论应该定期有一个适当的论坛。您可以在需要的时候修改文档,只要您将这些更改传达给所有需要知道的人。如果你把你的 CSS 指南放在一个 wiki 中(参见下面工具标题下的 wiki 部分),它们通常会通过“被关注的页面”或类似的方式支持某种通知机制。这意味着,任何未明确标记为“次要”的修订,都会向订阅者列表发出变更警报。组织内从事 CSS 工作的任何人都应该是这个列表的成员。他们还应该能够自己修改指南(并依次通知其他人)。但是,一定要有人拥有这份文件,也就是说,一定要有人对文件中发生的变更负责,并确保其他人知道对文件所做的任何变更。

比方说, RGBA(红绿蓝阿尔法)的颜色声明方法最近在我们的 CSS 开发人员中很受欢迎。在我们想象的组织中, Igloo 冰箱部件公司,我们所有的前端开发人员每月召开一次会议。在本月的会议上提出并同意这种方法有价值,我们想开始使用它。使用 RGB(红绿蓝)作为十六进制代码的直接替换被发现在我们希望保持足够功能的浏览器中有很好的支持(参见第六章中的“分级浏览器支持”),并且RGBA给了我们以前没有的能力(特别是 alpha 透明),所以我们想把它添加到我们的 CSS 格式指南中,并修改我们以前的建议。同样,这是一个例子,不是我们的建议。

以前,我们有这样一句话:

  • 所有颜色都应该是十六进制格式:#123456

我们现在可以这样修改它:

  • 所有颜色都应该是 RGB 格式:rgb(100,100,100)
  • 在颜色需要支持 alpha 透明的地方,我们应该使用 RGBA 格式:rgba(100,100,100,0.5)

images 提示:总是先声明一个非 alpha 的 RGB 颜色,不要依赖于旧版浏览器的 alpha 透明度。

在会议上,有人被赋予了做出这些修改的责任,并确保每个人都被告知这些修改。请注意,这并不意味着我们应该立即重新访问我们所有的 CSS 代码,并将所有的十六进制颜色转换为 RGB,但这确实意味着任何未来的颜色都应该是 RGB 格式的。下一次我们有机会重构的时候,就是转换遗留代码的时候了,以满足新的指导方针。

通常,在较大的组织中,不同的部门之间不会相互交流,而且业务可能如此之大,以至于他们甚至从来不知道其他部门的存在。我们鼓励你去接触你的企业中写 CSS 的每一个人,试着让每个人都说同一种语言并一起工作。

工具

有许多工具可以帮助我们开发网站和管理流程。有无数的软件来管理我们的任务,存储我们的文件,存储文档,创建模型,共享文件,开发我们的代码,相互通信,等等。本节讨论了一些可能对您的流程有用的软件类型,并提到了每种类型的一些例子。不可能提及(并使用过)所有的东西,所以请将这些工具视为一个潜在的候选列表,并注明重要的功能,而不是一个完整的概述。在决定特定的选择之前,先回顾一下可用的选项。

维基百科

一个维基是一个软件(几乎总是基于一个网络浏览器),作为一个文档库,并允许特定人群编辑它们。它们通常包括一些功能,如“被关注”的页面,允许用户在更改时收到通知,以及存储文档以前修订的版本信息。维基最著名的例子当然是著名的[www.wikipedia.org](http://www.wikipedia.org)。将所有文档以组织有序的方式集中存储有很多好处:

  • 易于备份
  • 在哪里定位特定类型的数据显而易见
  • 数据不会过时或发生冲突(每个人都处理同一条信息,而不是在多个地方有多个版本)
  • 潜在远程可用
  • 独立于平台
  • 文档以前的修订不会丢失

维基很容易失去控制。确保有人负责拥有 wiki,执行合理的结构化分类,并执行内务管理。存在许多 wiki 软件的例子。有些是托管的,有些你可以自己托管,许多是免费的,许多是付费的…他们使用许多不同的语言来格式化。 2

这里有一些最丰富和众所周知的。


一种被称为克里奥尔语的标记语言旨在标准化维基中使用的语言,并已被许多不太知名的维基所采用(尽管不一定是默认语言)。你可以在 www.wikicreole.org/的 ?? 读到克里奥尔语。

媒体维基

可以说是最著名的维基软件——这是维基百科背后的维基。MediaWiki ( [www.mediawiki.org](http://www.mediawiki.org))是开源的,高度可配置的,并且设置简单。它支持大多数数据库,并且是用 PHP 编写的。插件可用。有些身份验证功能是内置的,但感觉还没有完成,没有经过测试。MediaWiki 还有一个非常慢的名声。通常情况下,使用开源软件的好处(它是免费的!)的不足之处在于,它给人的感觉像是开源软件。

亚特兰提斯的汇合

Confluence ( [www.atlassian.com/software/confluence](http://www.atlassian.com/software/confluence))不是免费的,但它是一个更加完善、功能更加全面的维基百科,拥有一个非常棒的所见即所得(WYSIWYG)界面。有一个托管解决方案,也有一个您可以在内部运行的解决方案。Confluence 是用 Java 编写的,支持大多数常见的数据库。Confluence 可以与 Atlassian 的其他产品巧妙地集成,并具有一些基本的社交网络功能。有一个很大的插件库。您可以(在撰写本文时)以 10 美元的价格为最多 10 个用户购买一个许可证,但是当您的需求超过这个数目时(在大型环境中很可能是这样),它很快就会变得更加昂贵。

Mac OS X 服务器 Wiki

如果您正在运行 OS X Server(从版本 10.5 以上),您已经安装了此 wiki([www.apple.com/server/macosx/](http://www.apple.com/server/macosx/))。默认情况下,每个用户组都有自己的维基,但是你可以创建更多。身份验证功能运行良好。Mac OS X Server Wiki 在用户界面后面使用 HTML 来格式化数据,但有一个非常严格的白名单来控制哪些标签和属性可用,这很快就会成为一个障碍。你可以修改这个列表,但是这样做没什么意思。它非常吸引人,非常精致,设置和使用都很简单,但是在功能上有点局限。

轨道

Trac ( [trac.edgewall.org/](http://trac.edgewall.org/))包括一个 bug/问题跟踪器,是一个专门针对软件开发项目的专用 wiki。Trac 将 bug 追踪器和 wiki 合二为一,这可能会简化您工作流程。它是开源和免费的,但是感觉不像这个列表中的其他人那么完美。

images 提示:比较不同维基的一个很好的资源在[www.wikimatrix.org](http://www.wikimatrix.org)

漏洞报告

错误报告软件的目的是给我们一个集中的地方来记录你的软件中发现的任何错误或缺陷。就像维基的情况一样,有些是托管的,有些可以自己托管,有些很贵,有些是免费的。Bug 追踪器用于测试人员提出不同优先级的 bug。 3

下面几节将讨论一些专门的错误报告软件的例子。

灯塔

Lighthouse ( [lighthouseapp.com/](http://lighthouseapp.com/))是软件即服务(SaaS) ,它提供了一个干净简单的界面,可以在你的浏览器上运行。它不是免费的,但价格合理。

布吉拉

Bugzilla ( [www.bugzilla.org/](http://www.bugzilla.org/))是 Mozilla 的一款免费开源 bug 追踪器。它功能齐全,成熟,对版本控制系统有很大的支持。它是用 Perl 编写的,支持使用 web、电子邮件、RSS、Web 服务和命令行的用户界面。MySQL 和 PostgreSQL 都支持作为数据库。

阿斯塔纳·希拉

JIRA ( [www.atlassian.com/software/jira/](http://www.atlassian.com/software/jira/))不是免费的,但是有一个庞大的功能集。它的管理可能有点令人生畏,但它足够灵活,可以处理几乎任何流程或工作流。安装和维护(尤其是升级)并没有想象中的那么简单,但是可用插件的质量大大弥补了这一点。

轨道

Trac ( [trac.edgewall.org/](http://trac.edgewall.org/))第二次被提及是因为它的缺陷跟踪能力。您可能会发现,将 wiki 和 bug tracker 结合起来对您的公司非常有用,Trac 在这两方面都非常出色。先前的批评仍然适用。

任务管理

任务管理软件让我们记录我们期望团队承担的任务。它可能包括资源/时间管理和综合报告功能等功能。这种软件的作用是为输入和检查任务提供一个简单的界面。企业任务管理软件(也可以称为项目管理软件)通常是围绕敏捷流程构建的。

下面几节给出了一些例子。


你可以在他的博客zeroedandnoughted . com/standardized-bug-reporting-with-SEERS/上了解 Antony 记录和优先处理 bug 的标准化方法,称为 SEERS(截图、环境、预期/实际行为、再现、严重性)。

事物

OS X 的桌面应用程序,Things ( [culturedcode.com/things/](http://culturedcode.com/things/))实际上是在个人层面上管理任务,而不是在团队之间共享。许多人发现这种额外的自我管理很有用。也有一个 iPhone 应用程序可以通过 wifi 与桌面版本同步(遗憾的是,不是通过云)。

集会

Rally ( [www.rallydev.com/](http://www.rallydev.com/))是一个托管和付费的解决方案。虽然不直观,缺乏一些功能,但当您的员工可能同时处理几个项目时,它可以很好地处理资源和时间管理。

打成一片

Mingle ( [www.thoughtworks-studios.com/mingle-agile-project-management](http://www.thoughtworks-studios.com/mingle-agile-project-management))是由 ThoughtWorks 开发的,ThoughtWorks 因其对 IT 行业敏捷运动的贡献而闻名。Mingle 包括一个精致的基于网络的界面,带有内置的 wiki。用户界面只在白板上的卡片上工作,并且流程是非常可定制的。定价是基于每个用户的,不太可能便宜,尽管最多五个用户可以免费试用一年。

错误跟踪和任务管理

有些解决方案可以同时满足这两个需求,而不是让这两个系统保持完全不同。这很有道理——我们的 bug 需要修复,修复它们当然是一项任务。在一个好的流程中,这些东西可以一起管理。需要维护的软件越少越好,如果开发人员只需要学习一个应用程序,那么培训成本就会降低。

下面是一些将任务管理和错误跟踪结合起来的软件的例子:

亚特兰蒂斯 JIRA(带绿色漏斗插件)

绿色漏斗([www.atlassian.com/software/greenhopper](http://www.atlassian.com/software/greenhopper))把 JIRA 变成了一个全功能的敏捷任务跟踪器。这是非常可配置的,很容易使 JIRA 类似于我们许多人习惯的白板和卡片。看板(一种不太常见的敏捷开发方法)也得到很好的支持。很难找到比这更好的设置,但是(对于较大的团队)它不便宜,也不容易管理、安装和维护。4

敏捷

Agilo ( [www.agile42.com/cms/pages/agilo/](http://www.agile42.com/cms/pages/agilo/))基于并支持与 Trac 的集成,为 SCRUM(一种敏捷开发方法)提供了一个全功能的解决方案。除了付费版本,还有免费版本可供选择。提供了安装程序,如果您熟悉 Python 的话,还提供了一个 Python 彩蛋,它是用。


我们不为亚特兰蒂斯人工作。我们真的很喜欢他们的作品。

牙床

Fogbugz ( [www.fogcreek.com/fogbugz/](http://www.fogcreek.com/fogbugz/))最初是 Fog Creek 软件的内部错误跟踪工具,于 2000 年首次发布。它已经扩展到包括维基、论坛和任务管理。它支持基于证据的调度(EBS) 5 ,并且有一个内置的 API 用于连接到你自己的软件。它可以作为托管解决方案(有 45 天的免费试用期)使用,也可以安装在您自己的服务器上,而且价格合理。

很容易达到软件过载。许多公司有三个或四个同时运行的 wikis,以及不同版本的任务或错误跟踪软件。这种情况非常低效,对流程有害,必须不惜一切代价避免。决定在你的业务中使用哪种软件,然后应用它。尝试使用严格的和可执行的规则来组织你的信息,而不使它变得太复杂。

images 提示:你的工具发送电子邮件的地方,如果你能这样配置的话,保持这封邮件最少、相关、切题是很重要的。如果你的开发人员不知所措,邮件就不会被阅读。

源代码控制

源代码管理有许多风格,但是每一种风格的核心都是维护项目中每个文件的历史(存储在一个存储库中)的概念,并且能够回滚到任何文件的先前版本。还有一个(典型的)概念,一个文件可能被许多人处理,因此需要被“合并”以包含每个人的更改。

我们项目中文件的主要副本被称为主干。当一个版本的软件被发布时(被认为可以安全地放入生产环境中),很常见的是标记存储库的当前状态,或者分支存储库(这意味着创建存储库当前状态的复制版本)。我们这样做的目的是,如果在生产中发现版本有问题,我们可以对代码进行修改,而不必担心可能会冲突或无法发布的最新更改。通常我们应该尽可能在主干中工作,因为合并分支之间的变更可能会有问题。

下面几节给出了一些源代码控制系统的例子。

Visual SourceSafe (VSS)

VSS ( [www.microsoft.com/visualstudio](http://www.microsoft.com/visualstudio))现在被广泛认为是一种遗留的和过时的源代码控制方法。当一个用户选择处理一个文件(通过签出)时,该文件被锁定,任何其他用户都不能处理,直到它被提交回存储库(签入)。

这种方法有一个好处:文件几乎不需要合并,因为它们总是处于开发人员满意的状态。然而,这意味着开发人员永远不能同时处理同一个文件,并且经常忘记提交他们的文件,从而对其他用户锁定文件。


乔尔·斯波尔斯基发明的一种更精确地评估任务的方法。点击[www.joelonsoftware.com/items/2007/10/26.html](http://www.joelonsoftware.com/items/2007/10/26.html)阅读更多信息

并行版本系统(CVS)

CVS ( [www.nongnu.org/cvs/](http://www.nongnu.org/cvs/))是 Dick Grune 在 1986 年 7 月开发的。尽管以前也存在其他类似于 CVS 的版本控制系统(这是第一个允许多人同时修改文件的系统)获得了广泛的接受。CVS 模型将所有文件保存在服务器上的集中存储库中,客户端从该存储库中取出文件并提交到该存储库中。

颠覆(SVN)

CVS 有许多缺点,尽管它今天仍被普遍使用,Subversion ( [subversion.apache.org/](http://subversion.apache.org/))是由 CollabNet 在 2000 年创建的,试图修复 CVS 中的许多错误,并添加许多非常需要的“缺失功能”。因此,它的运行方式与 CVS 非常相似。由于其相对的稳定性和成熟度,它是当今公司中最常见的版本控制系统之一。

走吧

Git 6 ( [git-scm.com/](http://git-scm.com/))是分布式版本控制的一种形式。这意味着开发人员在自己的机器上有一个存储库,以及一个(或多个)集中存储的存储库。用户不会因为害怕提交错误的代码而“囤积更改”(因此增加了由于崩溃或硬件故障而丢失数据的可能性),而是将他们的代码提交到他们的本地存储库,并可以从那里管理修订。Git 不是一个单独的库,它实际上是由一系列独立的工具组成的,利用这些工具,你可以实现你喜欢的任何版本控制流程。尽管 Git 中使用的合并算法很受推崇,并且已经通过同时合并 12 个 Linux 内核补丁得到了验证,但是 Git 是一个复杂的系统,很难理解,而且对于您的项目来说可能有些矫枉过正。 7

水银

另一个分布式版本控制系统 Mercurial ( [mercurial.selenic.com/](http://mercurial.selenic.com/))有时以其命令行名称而闻名:hg。它由 Matt Mackall 开发,于 2005 年发布。Git 和 Mercurial 是同时开发的,因为免费版的 BitKeeper——Linux 内核项目当时使用的源代码控制软件——被它的开发者 BitMover 撤回了。尽管 Git 最终被 Linux 内核项目所使用,Mercurial 仍然受到业内许多人的拥护。与 Git 相比,Mercurial 功能不全,但仍然非常实用、快速且易于使用——特别是对于那些从 Subversion 迁移过来的人。

images 注: BitBucket ( [bitbucket.org/](http://bitbucket.org/))是一个类似于 GitHub 的服务,用于 Mercurial 项目。


在英国英语俚语中,Git 是一个不愉快的词,用来称呼同样不愉快的人。Git 的创造者 Linus Torvalds 说,“我是一个自私的混蛋,我以自己的名字命名所有的项目。先是 Linux,现在是 git。”


7 GitHub ( [github.com/](http://github.com/))提供可能适合你的项目的免费公共库。

图形用户界面

几乎所有的版本控制系统都带有命令行界面,但是存在许多图形用户界面(GUI)以及插件,以使集成开发环境(ide)和其他软件能够与它们通信。提及所有这些超出了本书的范围,但是值得一提的是用于 Windows 平台的龟壳扩展家族(TortoiseCVS、TortoiseSVN、TortoiseHG 等等)。它们与 Windows 紧密集成,在文件系统资源管理器中的右键菜单中添加了功能,以及表示状态的图标,这是它们最直观的位置。OS X 和 Linux 的模仿版本已经尝试过了,但是没有一个(在撰写本文时)像乌龟一样紧密和直观地集成在一起。

使用版本控制系统

当使用源代码控制时,当你对你所做的感到满意时,你提交文件到存储库。这将使用您正在处理的版本更新中央代码。如果您正在使用分布式版本控制,您应该经常提交到本地存储库,并且只有在您确信您的代码工作正常时才提交到中央存储库。在图 1-2 和图 1-3 中可以看到版本控制和分布式版本控制的直观对比。

images

图 1-2。普通版本控制图

images

图 1-3。分布式版本控制图

当你在团队中处理 CSS 时,使用版本控制系统是至关重要的。它给了你更多的信心去处理其他开发人员可能正在处理的文件,以及我们提交的每个变更的可恢复历史的保证。当我们使用 CSS 的时候,我们经常分成小块工作。我们做一些小的改变,切换到我们的浏览器,刷新页面,改变,切换,F5,等等。我们应该记得经常提交我们的变更,这样我们就可以对我们所做的一切有一个好的历史记录。

当提交您的更改时,大多数版本控制系统会要求您提供一个描述您所做更改的注释。当您的版本控制系统进一步嵌入到您的流程中时,这些注释变得非常有价值。因此,最重要的是你的评论是相关的,描述性的和有帮助的。

像这样的评论没有什么价值:

Changed some bits and pieces.

但是像这样的注释准确地描述了文件中发生的事情:

Changed hex colors to RGB colors.

当事情出错时(它们会出错),您会发现自己在查看有问题的文件的提交历史,有用的注释将非常有助于澄清每个修订中的更改。

通常,鼓励您在开始工作之前手动更新文件。这意味着从存储库中获取最新版本。如果您未能更新文件的过期版本并开始工作,或者如果另一个开发人员在您开始工作后提交了更改,则您需要合并这些文件。因为两个开发人员同时工作,所以您的本地副本和存储库中的版本之间的变化是非线性的。不可能以自动的方式确定保留哪些部分和替换哪些部分;这导致文件发生冲突。为了解决冲突,有必要定位差异,适当地编辑文件,然后将冲突标记为已解决,以便您可以提交文件的修改版本。

差异工具

为了处理这些冲突,您将希望确切地看到这两个文件之间的差异。有一种特殊类型的应用程序可以做到这一点:一个 Diff 程序。再说一次,它们中有很多是存在的,我们不打算提升其中一个。这些程序的目的是比较两个相似的文件,并显示它们之间的差异。

目的是使识别和解决同一文件的两个版本之间的冲突变得容易。因为 CSS 通常不像我们希望的那样模块化(或者可能在意想不到的地方产生影响),尽管冲突可以解决,但我们仍然应该尽可能避免它们。最好的方法是与你的团队进行有效的沟通,让每个人都确切地知道你在任何时候都在处理哪些文件。

在这些冲突存在的地方,我们必须合并并解决它们。解决冲突和合并变更是让多个个体处理同一个文件的一些最头痛的事情。老式的命令行比较程序只显示文件之间的差异,类似于图 1-4 中显示的输出。

images

图 1-4。命令行比较工具的输出示例

更现代的 Diff 程序通过让你并排查看两个文档来减轻这种痛苦。两个文档之间的差异被突出显示,任何不同的部分都可以从一个文档复制到另一个文档(参见图 1-5 )。

images

图 1-5。一个基于 GUI 的比较工具的输出示例

通常,在您的 IDE 或版本控制系统中会提供简单版本的 Diff 工具,但是下面的部分描述了其他专用 Diff 程序的示例。

习语合并

Araxis Merge ( [www.araxis.com](http://www.araxis.com))标准版可以比较两个文档,专业版可以同时比较合并三个独立的文档。它价格合理,可用于 Windows 和 Mac 平台,在这两个平台上都有很好的性能。

WinDiff

WinDiff ( [support.microsoft.com/kb/159214](http://support.microsoft.com/kb/159214))包含在 Microsoft Visual Studio 中,因此如果您已经使用该平台进行开发,它是免费的。如果您运行的是 Windows XP,您也可以从[www.microsoft.com/downloads/details.aspx?familyid=49ae8576-9bb9-4126-9761-ba8011fabf38&displaylang=en](http://www.microsoft.com/downloads/details.aspx?familyid=49ae8576-9bb9-4126-9761-ba8011fabf38&displaylang=en)下载它,但它包含在 Windows 版本(包括 Windows 2000 和更高版本)的光盘中。

WinMerge

WinMerge ( [winmerge.org/](http://winmerge.org/))顾名思义就是 Windows 应用。它是开源的,可以免费获得,并且性能良好。

无与伦比

Beyond Compare ( [www.scootersoftware.com/](http://www.scootersoftware.com/))是由 Scooter Software 开发的,已经是第三版了。它适用于 Windows 和 Linux。它价格合理,有很好的声誉和特性集。

变化

Changes ( [connectedflow.com/changes/](http://connectedflow.com/changes/))是一个基于 Mac 的应用程序,可以很好地与 OS X 上的其他应用程序集成在一起。图 1-5 显示了实际操作中的变化。它价格低廉,可以比较文件夹以及文件。

万花筒

万花筒([kaleidoscopeapp.com/](http://kaleidoscopeapp.com/))值得一提,是一个非常有吸引力和直观的差异工具。它是由 Sofa 公司开发的(该公司也使版本 SVN 成为 OS X 的客户端软件),是一个漂亮而直观的低价软件的典范。不幸的是,从版本 1(撰写本文时的当前版本)开始,它不包含合并功能,这可能会限制它对您的用处。

更完整的比较工具列表可在[en.wikipedia.org/wiki/Comparison_of_file_comparison_tools](http://en.wikipedia.org/wiki/Comparison_of_file_comparison_tools)获得。

解决冲突

让我们演示一下如何解决 SVN 冲突。我们将在 OS X 上使用版本 SVN 客户端和更改比较工具。为了举例,我们正在处理一个专门用于 Internet Explorer 8 的文件,名为 ie8.css。我们已经更新了本地存储库,然后在本地处理该文件。该文件的内容最初如下:

`#imagePath {
   filter: alpha(opacity=0);
}

tryit-form .input-file {

height:18px;
   width:217px;
}`

我们添加了一个额外的选择器,并改变了#triyit-form .input-file的高度和宽度,所以文件现在看起来像这样:

#imagePath {    filter: alpha(opacity=0); } **img.example {**
`**   float:left;**
}

tryit-form .input-file {

height:22px;
   width:220px;
}`

我们测试了这些更改,它们工作正常,所以我们用适当的注释提交文件(见图 1-6 )。

images

图 1-6。一个 SVN 提交对话框的例子

结果如图 1-7 所示。

images

图 1-7。一个 SVN 冲突对话的例子

这个图告诉我们,自从我们上次更新它以来,这个文件已经被修改和提交了。正如所建议的,我们现在将对我们的本地副本进行另一次更新,以拉下最新的更改(见图 1-8 )。

images

图 1-8。文件浏览器窗口中的 SVN 冲突

版本现在显示几个新的文件和图标。ie8.css旁边有一个感叹号,表示目前处于冲突中。但是三个全新的文件也出现了:

  • ie8.css.r313
  • ie8.css.r312
  • ie 8 CSS 地雷

这些文件中的每一个都有其用途:

  • ie8.css.r313是存储库中此文件的最新版本。
  • ie8.css.r312是我们上次更新时从存储库中下载的文件版本。
  • ie8.css.mine是我们刚刚尝试提交的文件的版本。
  • ie8.css现在包含了标记,我们创建的版本,以及存储库中的变更。

内容看起来像这样:

`#imagePath {
   filter: alpha(opacity=0);
}
img.example {
   float:left;
}

tryit-form .input-file {

<<<<<<< .mine
   height: 22px;
   width: 220px;

height: 24px;
   width: 320px;
   float: left;
}
.clearfix {
   zoom: 1;

.r313
}`

标记显示发生了什么变化。更新后<<<<<<< .mine=======之间的所有内容都被更改过,=======>>>>>>> .r313之间的所有内容都被别人更改过。请注意,SVN 并没有暗示我们的img.example选择器和规则有冲突,因为它足够聪明,可以自己解决和合并这些更改。

为了解决这个问题,让我们比较一下我们的版本和存储库中的最新版本。首先,我们在变更中打开这两个文件(见图 1-9 )。我们需要考虑将这些文件中的一个作为主文件,所以我们将使用.mine文件,并确保首先选择该文件,使其出现在左侧。

images

图 1-9。更改 app 文件选择对话框

结果如图 1-10 所示。

images

图 1-10。变更应用中的文件比较

文件中的差异现在非常明显。我们为选择器img.example添加了一个部分,别人添加了.clearfix,另一个选择器的内容被修改了。我们快速检查日志,找出谁修改了这个文件,并阅读他们的提交注释。如果有必要,我们可以联系他们,询问他们的变化。原来,设计的改变需要改变特定元素的高度和宽度,而我们对它们的改变不再是必要的。使用变更中左边的文件作为我们的主文件,我们现在合并我们的变更。我们只想要文件的底部,所以我们点击右边文档中该部分的任何地方,然后点击复制到左边(见图 1-11 )。

images

图 1-11。在变更应用中合并文件

左边的文件现在是我们想要的样子。我们将内容复制粘贴到ie8.css file中,在版本中标记为“已解决”(见图 1-12 )。

images

图 1-12。解决变更 app 中的冲突

最后,我们从存储库中进行另一次更新,并测试该文件以确保它能够正常工作。确实如此(希望如此!),所以我们把它交给 SVN。 8

备份

不言而喻,备份是任何开发流程的重要元素。备份存储库和其他重要数据非常重要。如果可能,这些备份的版本应该存储在几个不同的位置。硬件故障、数据损坏以及火灾或洪水损坏可能会在瞬间毁掉数月甚至数年的工作。

备份应该像保险一样对待。你几乎从来不需要它,但是如果你需要,它可能比实现它的成本更有价值。您还应该有不同日期的备份,比如在过去七天的任何时间点每天备份一次,甚至可能是更早的每月备份。有时我们发现我们想要恢复到几天前丢失的版本或者查看产品的遗留版本,并且不是所有的东西都将被存储在源代码控制中。


对于这个例子来说,创建一个冲突实际上是相当困难的,这表明 SVN 在合并文件方面做得很好,除非你想让它这么做。

确保您的备份切实有效非常重要。虔诚地记住将不同的备份磁带带回家固然很好,但如果您不运行测试恢复,您可能会在最糟糕的时候发现所有的努力都白费了。

开发人员的机器没有您的存储库和服务器重要。简单的硬盘备份就足够了;目标是当硬盘出现故障(这是不可避免的)时,能够让开发人员尽快恢复运行。

images 注意:硬盘失败。不是的话的问题,而是的时候的问题。做好准备。

在线备份

有几个在线备份示例,通常提供免费和付费服务。一些例子包括如下:

  • Dropbox(“??”)
  • 活网([www.mesh.com](http://www.mesh.com))
  • 白痴(1230)
  • spiderak([spideroak.com/](https://spideroak.com/)

它们都支持 Windows 和 OS X(在许多情况下还支持 Linux),并允许你与其他用户分享你的内容。某种脚本操作可以从您的本地机器复制到它们中的每一个,如果您没有太多数据(开发人员通常只需要备份他们机器上的一个或两个文件夹),它们通常是免费的。如果你的员工在家或远程工作,能够共享大文件通常是非常有用的,尽管许多大公司不喜欢他们的任何文件在其他公司的服务器上。

桌面备份

如果您想要一个自动化程度更高的现成解决方案,这里也有很多:

  • Microsoft NTBackup/Windows 备份和恢复中心/Windows 服务器备份(某些 Windows 版本免费)
    • ??[www.microsoft.com/athome/setup/backupdata.aspx](http://www.microsoft.com/athome/setup/backupdata.aspx)
  • 时间机器(免费提供 OS X 的最新版本)
    • ??[www.apple.com/support/leopard/timemachine/](http://www.apple.com/support/leopard/timemachine/)
  • 赛门铁克备份执行
    • ??[www.symantec.com/business/products/family.jsp?familyid=backupexec](http://www.symantec.com/business/products/family.jsp?familyid=backupexec)
  • Roxio Retrospect
    • ??[www.retrospect.com/](http://www.retrospect.com/)

事实上,备份选项比我们在此列出的要多得多。更完整的备份软件列表和比较可以在[en.wikipedia.org/wiki/List_of_backup_software](http://en.wikipedia.org/wiki/List_of_backup_software)找到。

原型制作

作为范围界定流程的一部分,通常有必要为我们将要构建的内容创建一个基本版本,以提供一个实际操作的示例,否则这可能是一个需要解释的复杂概念。我们对产品质量代码的考虑——如可访问性、文件大小、浏览器支持等——在这里并不重要。我们正在开发的代码只能在内部看到,我们可以指定浏览器的要求。任何框架(在第四章阅读更多关于框架的内容)和加速开发的捷径在创建原型时都是完全可以接受的。

通常完整的原型被认为足够好,成为产品代码,尽管没有其他代码写得好。这应该避免,因为它鼓励代码膨胀和技术债务,以及其他 CSS 罪恶。为此,以某种方式削弱原型通常是一个好主意,这样我们肯定会有机会在以后重构和重写它。

如果我们想要原型化纯视觉的东西,比如一个站点的布局或用户旅程,有很多工具可以帮助我们。一个很好的例子是 Balsamiq Mockups ( [www.balsamiq.com/products/mockups](http://www.balsamiq.com/products/mockups)),它的好处是创建的图可以是功能性的,但看起来仍然像草图(这是 Comic Sans 唯一有效的用途之一!).它们不能被误认为是生产图像,讨论的重点是内容和布局,而不是像字体和颜色这样的东西。

尖峰类似于原型。一个尖峰如此命名是因为它是我们当前路径的切线的可视化描述。如果在开发流程中,我们发现一些我们需要开发的东西,并且我们不确定要遵循的最佳路径,我们可以在那一点上“加速”开发。我们将创建一个非常粗略的例子来演示我们想要的路径,并证明它是可行的。这个例子除了展示我们的方法将达到我们想要的效果所必需的部分之外,不会包括任何东西,并且从定义上来说(除非它是非常直观的东西,否则我们会被扣球)是没有吸引力的和有限的。虽然原型是我们可以复制、粘贴和修改以满足我们的需求的东西,但 spike 在被证明(或未被证明)后总是会被丢弃。

开发方法

存在许多开发方法和流程。它们中的每一个都有自己的一本书,但是我们将很快地触及两个最流行的类型(很远),瀑布和敏捷。

瀑布开发

瀑布模型源于制造业。虽然瀑布这个词从未被使用过,但这种模型的第一次官方描述通常被认为来自温斯顿·w·罗伊斯在 1970 年写的一篇文章。罗伊斯实际上是在写这个模型,以指出在他看来,这是一个有缺陷和有问题的工作方式。

瀑布(在软件中)描述了一种遗留的开发方法,尽管在许多组织中仍在使用。就其最基本的形式而言,它是为我们打算交付的内容生成技术规范的流程,给出我们的资源(人员)所需的工作级别(时间段)的细节,以及我们将要构建的解决方案的精确细节的非常精细的描述。一旦对规范达成一致,开发人员就致力于准确地交付规范中的内容,直到它完成,在这一点上,它被呈现给企业或客户。此时,它要么被拒绝,然后进行修改,要么被签署并标记为已批准。

这些阶段可以这样描述(见图 1-13 ):

  • 需求收集(或范围界定)
  • 设计
  • 实现(或构建)
  • 验证(或测试)
  • 维护(或支持)

术语瀑布的产生是因为一旦我们到达一个阶段,就不可能改变(或返回)前一阶段的输出。那就是我们只能朝一个方向流动。

images

图 1-13。瀑布流程示例

与所有方法一样,瀑布开发也有优点和缺点:

  • 赞成的意见
    • 业务/客户对预期成本和时间表有明确的指示。
    • 对于正在建造的东西,有一个确切的定义。
  • 骗局
    • 在构建开始之前,规范通常是不完整的,或者有太低级别的错误而无法隔离。这导致开发团队和客户争论解决方案的特定部分是否包含在最初的成本计算中。如果不把规范做得非常精细和详细,对客户/企业来说显而易见、隐含和直观的项目可能会被开发人员误解。
    • 有一个悖论:要写一个完全准确的规格说明,需要作者已经建立了解决方案。
    • 规范没有为流程中的变化留有余地;如果企业/客户发现了另一个必须包含的需求,那么有必要等到“瀑布”干涸后再考虑这个需求。

无论您采用哪种开发方法,很少会发现一个企业或客户不是以瀑布方式工作的。当我们作为一个企业需要建造一些东西时,我们需要知道它要花多少钱,要花多长时间。为此,通常从他们的角度将项目视为一个瀑布,但是在项目生命周期的内部使用不同的方法。

敏捷开发

敏捷开发已经流行了 10 到 15 年了。敏捷开发的核心是团队应该敏捷的概念,也就是说,团队应该能够轻松地切换和改变他们的流程、概念和任务。敏捷方法基于迭代开发,并允许团队反思每个时间段内的成功或失败,修改他们的流程并快速响应需求的变化。在 2001 年发表敏捷宣言之后,许多早期的敏捷工作流,如 SCRUM 和极限编程,现在被称为敏捷方法。

敏捷宣言可以在[agilemanifesto.org/](http://agilemanifesto.org/)找到,但是为了清楚起见,我们把它放在这里:

我们通过自己动手和帮助他人来发现开发软件的更好方法。通过这项工作,我们开始重视

  • 流程和工具之上的个人和交互
  • 综合文档之上的工作软件
  • 合同谈判中的客户协作
  • 响应变化而不是遵循计划

也就是说,虽然右边的项目有价值,但我们更看重左边的项目。

以下是敏捷开发的一些优点和缺点:

  • 赞成的意见
    • 团队能够快速地改变和应对新的或变化的需求。
    • 这个流程一直在发展,团队的每个成员都可以在这个发展流程中发表意见并发挥作用。
  • 骗局
    • 通常很难或不可能承诺固定的截止日期。
    • 规范可能太松散,遗漏的特性发现得太晚。

尽管存在许多敏捷开发方法,但它们都促进协作和易于适应的流程。以下部分展示了敏捷实践的例子。

测试驱动的开发

测试驱动开发(TDD) 是在我们编写代码之前编写自动化测试(基于我们的需求和验收标准)的流程,并且只编写帮助我们通过这些测试的代码。这有助于最小化范围蔓延(新需求的产生)和不必要的代码。无论何时发现软件中的错误,我们都可以立即添加一个测试来重现这些错误。如果这个测试仍然在我们的测试套件中,我们可以确定我们不会在不知道它的情况下倒退。你可以在第十章中阅读更多关于测试的内容。

代码重构

重构我们的代码意味着重新访问它,并确保它尽可能地高效、快速和干净,并且不会留下任何潜在的代码味道。

images 注: 代码气味是马丁·福勒和肯特·贝克在福勒写他的书重构:改进现有代码的设计时讨论的一个术语。代码气味是一段代码,它表明了更深层次的问题。

任何关于“要做的事情:解决这个问题!”应该在代码重构期间解决。代码评审可以帮助促进团队之间的知识共享。代码重构通常是一种奢侈;如果你有机会做这件事,一定要充分利用它。这也可以被认为是偿还你的技术债务。尝试去除任何遗留代码,并考虑是否有其他方法可以格式化 CSS,以便最大限度地利用最小化算法或在浏览器中获得更好的呈现性能。

持续集成

持续集成 (CI) 服务器构建并测试我们的代码,并执行其他自动化任务,以响应版本控制存储库的提交或以预定义的时间间隔执行。我们将在第九章的中进一步讨论这个问题。

结对编程

许多公司现在提倡结对编程流程:与另一个开发人员一起工作,以避免知识囤积,指导和帮助团队的其他成员,因为这减少了成品中出现错误的机会。三个臭皮匠顶个诸葛亮。其他一些公司认为让两个开发人员同时从事同一项工作是低效的。我们建议,当一项工作足够复杂时,结对编程可能是非常宝贵的,但通常代码审查就足够了。在团队之间轮换开发人员是避免知识囤积的另一种有效方法。

策划扑克

计划扑克(有时被称为计划游戏)是一种针对任务收集资源估计的方法。它被称为扑克游戏,因为它使用卡片或代币来代表投票。在合理详细地描述了要评估的任务之后,特定领域的每个人(无论是服务器端开发、客户端开发、测试人员、设计人员等等)都会拿出一张卡片,上面显示了他们期望一个人完成任务所花费的时间。价值最高的人证明了他们的选择,价值最低的人证明了他们的选择,我们重复这个流程,直到我们有一个一致的决定。

规划扑克可能是一项非常耗时的工作,并不一定适合所有公司,但它有利于确保团队的每个成员都很好地理解我们正在交付的东西,并可能考虑和想到可能会滑入实现阶段的错误。通常,时间估计由卡片或令牌以不同的方式表示;例如,作为故事点或反映斐波纳契数列的天数(1、2、3、5、8、13 等等)。

images 注意:斐波那契数列实际上是从 0,1,1 开始的,但显然在这个例子中会适得其反。

代码审查

代码评审与结对编程相似,因为它们为其他开发人员提供了一个对已经编写好的代码进行评论的机会。通常这种学习是双向的,评审者会问为什么事情会以一种特定的方式完成,并提供反馈。就时间而言,它比结对编程成本更低,并且是在较小的团队中保证代码质量的更有效的方式。

每日站立会议

只要整个团队都有空,这些会议每天都会召开。在这些会议中,我们使用鸡和猪的概念。这个术语来自一个古老的笑话:

一天,一只猪和一只鸡正在聊天,这时鸡说:“嘿,我在想我们应该一起开一家餐馆!”

“我们应该吗?我们会叫它什么?”猪问。

"“火腿鸡蛋”怎么样?"鸡肉表明。

“不用了谢谢!”猪惊呼道。“我会承诺,但你只会参与!”

猪是实际建造项目的人。一只是有人参与了这个项目。一只鸡(项目经理,或者 scrum master)应该促进每日站立,但是只有猪应该有所贡献。这很有帮助,因为鸡应该对整个项目的成功感兴趣,而每只猪可能有自己的偏见或议程。通常猪是开发者,鸡是其他人。在每日站立时,每只猪应依次说以下内容:

  • 他们昨天在做什么
  • 他们今天在做什么
  • 如果有任何问题影响他们的工作能力

为了避免长时间的谈话,有一个非常有用的问题,我们可以在任何时候问:“每个人都关心吗?”

  • 如果在每日例会上有影响到每个人的问题,他们应该解释清楚,主持人应该鼓励讨论来解决问题。
  • 如果有只影响某些人的问题,主持人应该让这些人留下来进一步讨论。这通常被称为将对话离线

每天的起立发言通常应该非常快——每人发言不超过一两分钟。

回顾会

在我们流程的特定点,进行回顾非常有用。这是一个机会来回顾我们到目前为止的流程,并在必要时改进或改变它们。回顾有许多不同的形式,但是每个团队可以自己决定。任何感兴趣的团体都可以参加回顾会,但是只有猪(见前面的每日站立会议)可以参与。回顾的重要组成部分如下:

  • scrum 主管或项目经理应该促进回顾。
  • 每头猪都应该参加回顾展。
  • 团队应该认可成功的个人和流程。
  • 团队应该确定流程中可以改进的地方或以前遇到的问题。
  • 对于提出的每一个负面观点,主持人必须定义一个行动来解决问题,并将这个行动分配给个人或他们自己。
  • 没有不好的反馈!团队的所有成员都必须感到有能力并欢迎参与。

典型流程的一个示例可能是:

  • 每只猪都有一堆便利贴。
  • 在一分钟内,每只猪在一张纸条上记下上一次迭代的积极点。
  • 主持人将这些笔记排列在墙上,将相似的项目组合在一起。项目越相似,团队作为一个整体对某件事的感觉就越积极。如果团队对个人做出了积极的评价,个人应该得到认可和表扬。
  • 在一分钟内,每只猪在一张纸条上记下关于前一次迭代的一个负分。
  • 对于每个负面项目,主持人讨论潜在的解决方案(行动)。
  • 当发现一个动作时,主持人会将其分配给相关人员。

回顾是任何流程中最重要的部分之一。需要有一种方式让开发者反馈他们的想法。通常他们会有非常有用和聪明的建议;有时,他们可能想对某些事情咆哮或发泄(为此提供一个论坛是很重要的)。如果可能的话,最好安排一个户外的回顾会,并结合更多的社交活动,比如团队午餐。放松的人会给出更诚实的批评和反馈。

已经有很多很多关于开发方法的书籍,在这一节中,我们实际上只是浏览了皮毛。

总结

这一章关注于我们可以用来帮助我们以最有效的方式进行开发的流程和工具。尽管流程因公司而异,而且工具通常是内部开发的,但我们讨论的基本原则仍然适用。我们的流程必须严格,以确保它们得到遵守,我们必须始终确保有一个“反馈循环”,以便我们的员工可以指出我们流程中的任何失败,并可以进行更改以反映这一点。

如果可能的话,整个团队应该从所有可用的风格和方法中挑选,并随着时间的推移继续完善它们。这些流程适用于所有类型的开发规程,尤其适用于 CSS 开发,但是它们构成了您的工作环境的基础。要建立一个好的团队,写出最好最高效的 CSS,你需要一个坚实的基础。

下一章将关注 CSS 格式指南和规则,我们可以具体应用于如何编写和构造我们的样式表。我们也将开始研究最佳实践,并讨论在高流量网站上工作时需要考虑的问题。

二、CSS 样式指南

每个 CSS 作者都有他喜欢的编写代码的方式。例如,有些人喜欢将每个属性和选择器写在单独的一行上,而有些人则喜欢将所有内容写在一行上。有些作者从不在选择器的最后一个属性上添加分号(因为这不是必需的);其他人喜欢删除冒号和属性值之间的空格。名单还在继续。

这些只是几个 CSS 创作因人而异的例子——看看任何 CSS 论坛,你会发现对这些话题的无休止的争论,还有更多。你也可能会得出结论,没有人是完全正确或错误的——在围栏的每一边都有有效的论点。

然而,当处理大规模样式表时——或者要应用于大规模网站的样式表,其中性能和健壮性是最重要的因素——个人偏好通常必须被搁置,以让位于更灵活、更一致的代码编写方式。

这种方法在开始时并不是最有效的(一个新雇佣的开发人员必须适应组织中使用的惯例,所以他不能在不熟悉它们的情况下立即开始编码),但是它将使员工更容易编辑和更新由其他开发人员创建的样式表,而不必花时间去理解(有时正确的词应该是“解密”)和熟悉其他人的 CSS 写作风格。

有一套约定也将使作者更容易知道走哪条路。尽管在创建 CSS 解决方案时总会有一定程度的主观性,但是如果有一个 CSS 样式指南,许多疑虑将会消失,因为有“规则”可循。检测错误、错别字和错误也变得更加容易。

这一章集中在最重要的几个点上,在这些点上 CSS 创作可以变得更加一致和有效。您将了解以下内容:

  • 为什么你应该有一个 CSS 样式指南
  • 格式化 CSS 的有效方法
  • 如何有效地使用 CSS 注释
  • ID 和类命名的最佳实践
  • 命名空间

CSS 样式指南

如果你正在制作一个 CSS 样式指南(正如我们推荐的那样),它将被团队的所有成员所了解和使用,它可以并且应该包括一个 CSS 如何被格式化的参考。这将使阅读他人创建的样式表变得更容易,并且将消除 CSS 作者在创建或编辑文件时常见的主观性。它还应该指出如何提出意见,并鼓励使用它们。

创建和实现 CSS 样式指南将会消除一些与编码 CSS 相关的不一致性,不同的开发人员有不同的编码样式,有时其他人很难阅读和理解。这减慢了开发过程,并增加了错误、冗余和代码膨胀。风格指南是避免这种情况的工具,它通过一致性考虑到代码的可重用性和效率,为开发高流量网站提供了一个简化的团队环境。

像 BBC(见图 2-1 )或 Drupal(见图 2-2)这样的公司都有 CSS 样式指南,这些指南是公开的,对任何创建者来说都是很好的参考。

images

图 2-1。 BBC 层叠样式表标准&指南([www.bbc.co.uk/guidelines/futuremedia/technical/css.shtml](http://www.bbc.co.uk/guidelines/futuremedia/technical/css.shtml) )

images

图 2-2。 Drupal CSS 编码标准页面([drupal.org/node/302199](http://drupal.org/node/302199) )

然而,重要的是要记住,指南就是指南,仅仅是指南。开发人员总是不得不自己做大部分的决定,即使有一个合适的向导。一个风格指南不应该试图完全去除主观性,因为那永远不会发生,应该允许一定程度的灵活性和自由度。

在本章接下来的几节中,我们将讨论 CSS 样式指南应该参考的一些方面。你可以在本书末尾的附录 1 中找到一个完整的 CSS 样式指南的例子。

CSS 格式

CSS 是一种(像 HTML 一样)松散格式化的语言,在解析它时通常会忽略空白,因此您可以使用任意多的空格、制表符或回车,以便以您认为最容易读写的方式格式化它。正因为如此,它适合以多种方式进行格式化。

尽管两种写作风格有相似之处,但在小细节上有如此多的变化,很难提出一个大家都同意是最好的明确解决方案。

我们能做的是向您展示最常见的选项,以及为什么有些选项比其他选项更好或更有效。毕竟效率是我们看这本书都想达到的,对吧?

单线对多线

如果有一个 CSS 讨论通常不会导致有用的结论,那就是关于不同风格的 CSS 格式。栅栏倾向于把那些喜欢把他们的规则写在一行上的人和那些喜欢把每个单独的财产声明在一行上的人分开。

这是一个单行 CSS 示例,其中选择器、属性和值位于同一行:

div { width: 200px; height: 300px; padding: 20px; background: #efefef; }

这是一个多行版本:

div {    width: 200px;    height: 300px;    padding: 20px;    background: #efefef; }

multiline 方法的主要参数如下:

  • 它更容易阅读,尤其是如果你不熟悉的文件。
  • 更容易进行文件比较(使用 Diff 工具,在第一章中有更详细的描述)。
  • 没有水平滚动。

单行 CSS 也有它的优势:

  • 文档中的行数更少
  • 选择器更容易扫描
  • 减小文件大小

为了保持一致,本书将使用多线方法。这一选择背后的理由如下:

  • 更容易对变更和注释进行注释。
  • 对于较小的文件,这种格式更容易阅读。
  • 我们假设 CSS 文件将在开发的后期被缩小或压缩(更多关于 CSS 缩小和压缩的内容在第八章中)。

尽管这并不意味着我们相信这是绝对最好的 CSS 格式样式,但我们同意这是一种提供了更清晰的方式来阅读选择器和属性的样式。它也更容易对特定的属性和值进行注释,这是处理多人共享的 CSS 文件时的一个重要因素,大多数开发人员更喜欢多行 CSS 而不是单行版本。

支持多行 CSS 的一个主要论点是单行 CSS 使得使用 Diff 工具变得更加困难,因为在某些情况下,这些工具显示每行文件版本之间的差异(见图 2-3 )。或者,如果他们找到实际的差异,可能是在一长行的末尾,需要水平滚动。

images

图 2-3。在本例中,在“变更”应用程序中,虽然该行被突出显示,但我们看不到实际差异。

好的 Diff 工具会突出差异,不管文件是如何格式化的(见图 2-4);当涉及到文件比较时,格式的争论实际上应该不是问题。的确,乍看之下,如果它们在屏幕之外,可能更难发现这些差异,但是这个问题并没有大到您应该改变您的做法。如果它确实给你带来了问题,你可能会发现一个不同的比较工具可能会有所帮助。

images

图 2-4。在万花筒应用程序中,查看差异的各种方法否定了这个问题。

缩进

CSS 文件中的缩进可以提高可读性,也有助于理解 HTML 文档的结构。

属性可以在选择器本身内缩进,这样更容易只扫描样式表上的选择器,如下例所示:

`section {
   width: 400px;
   font-size: 14px;
   float: left;
}

footer {
   border-top: 1px solid #000000;
   font-size: 12px;
   clear: both;
}`

有些作者选择根据应用样式表的 HTML 文件的文档树来缩进选择器:

`section {
   width: 800px;
}

section article {
      border-bottom: 1px solid #999999;
      padding-bottom: 10px;
   }

section article footer {
         font-size: 11px;
         font-style: italic;
      }`

即使使用这种格式化方法,文档的结构变得更加清晰,它也很容易变得过于复杂或主观,当标记复杂时,您的规则可能会从页面的右侧消失。我们建议,如果你觉得这是对你的样式表的一个很好的补充,在你指定你的文档树的主要分割块的部分使用它,不要试图在其他部分复制它,如链接或排版。

制表符与空格

制表符对空格的争论远比 CSS 开发更古老,而且不太可能在本书中得到解决。然而,我们可以解释每个的优点和缺点。

缩进代码时,您可以选择使用制表符或预定义数量的空格作为单级缩进。大多数文本编辑器允许您在保存文档时选择将制表符转换为一定数量的空格(软制表符)或用不同的宽度表示制表符。有些人喜欢用两个空格来表示他们的缩进,有些人喜欢用四个空格,有些人甚至有比这些更复杂的个人规则。

使用选项卡的好处如下:

  • 单个字符比几个空格占用的文件空间少。
  • 通常可以在文本编辑器中控制单个制表符的宽度(这样开发人员可以选择它的可视显示方式)。
  • 如果您没有使用等宽字体,您的代码仍将正确缩进。

使用空格的好处包括:

  • 无论使用哪种编辑器,缩进的宽度都是一样的。
  • 可以相信空格总是出现在同一个地方,而制表位可能会因编辑器而异。
  • 环境之间的空间是一致的。

我们将提供的结论(非常主观)如下:

  • 使用制表符缩进。
  • 使用等宽字体。
  • 使用空格进行对齐。

如果你和你的团队不同意(看起来有二分之一的可能性),这不是问题。我们的目标是团队中的每个成员都做同样的事情,无论是空格还是制表符。一致性始终是我们最关心的问题。

冒号和分号

为了追求更小的文件大小,一些作者选择省略 CSS 规则中最后一个属性后面的分号。所以代替这个的是:

img {     border: 2px solid red;     float: left; }

我们会有这个:

img {     border: 2px solid red;     float: left }

事实上,如果文件大小是一个主要问题,我们也可以省略选择器之间的空格和冒号与每个属性值之间的大括号,以及最后一个属性值后面的最后一个空格——这仍然有效,尽管会使它更难阅读:

img{border:2px solid red;float:left}

然而,当走向这些极端时,我们正在接近缩小对 CSS 文档的影响。

在大型网站工作时,性能是一个持续关注的问题;压缩和缩小你的 CSS,使其具有尽可能最小的文件大小,这应该是开发过程中的一个步骤。(你可以在第八章阅读更多关于缩小的内容。)在这一节中,我们所指的是当一个 CSS 文件仍在被编辑时,当文件大小不是一个问题时,如何格式化它,至少在是否添加最后一个分号或是否在冒号后有一个空格的层次上是如此。

和以前一样,无论你做什么决定,都需要你的团队坚持。假设文件将被缩小,当你在处理文件时,你应该主要关心它们的易读性和对团队最有利的东西。

注释和 CSS 元数据

CSS 注释经常被作者遗忘或忽略,但是如果它们是一致的并且是最新的,它们会非常有用。

这里有一个简单 CSS 注释的快速提示:

/* This is a CSS comment */

images 注意:您可能会注意到这与 JavaScript 和许多其他语言中的块注释相同。不幸的是,CSS 不支持单行注释,如下所示:

//This is a CSS comment

但是有一些选项比如 LESS (Leaner CSS)可以帮你实现这个(见第九章)。

可以内联添加注释以对特定属性进行注释,例如:

.myClass {     color: red; /* Same color as logo */ }

或者在单独的行上,例如,将选择器分成有意义的部分:

`/* Table headings */

thead th {
        font-weight: bold;
}
tbody th {
        font-weight: normal;
}

/* Table rows */

.odd {
        color: red;
}
.even {
        color: blue;
}`

CSS 注释可以跨越任意多行。当 CSS 文件被缩小时,注释往往是第一个被剥离的东西(我们在第八章中更详细地讨论了缩小),但是在开发阶段,注释有许多不同的用处。

现有标准:CSSDOC

已经有一些创建 CSS 文档标准的尝试。一个例子是 CSSDOC,它是 Javadoc(一个将 Java 源代码中的注释提取到 HTML 中的文档生成器)的改编版。

CSSDOC 使用 DocBlocks ,这是从 PHPDocumentor 项目借用的术语。文档块是人类和机器可读的数据块,具有以下结构:

/**  * Short description  *  * Long description (this can have multiple lines and contain <p> tags  *  * @tags (optional)  */

DocBlock 以/**开头,以一个空格后跟/(像普通的 CSS 注释一样)结尾。每行必须以一个空格开头,后跟一个星号(),否则它将被文档生成器忽略。标签以星号开头,然后是它们的名称(没有空格)。标签可以包含诸如@author、@copyright、@todo 等信息。它们的描述或值应该以一个空格开头,紧接在标记名之后。

CSSDOC 标准建议 CSS 文件应该在文件顶部包含一个文件注释,它应该包含与整个文件相关的元数据。此文件注释应包括文档标题等信息;一段描述;和标签,如@project、@version、@author、@copyright,甚至@colordef 标签,指示文件中使用了哪些颜色。

文件注释后面可以跟零个或多个部分注释,将样式表分成相关的块。黑客攻击和错误修复应该放在每个部分的末尾。部分注释包含标签@section,如下所示:

/**  * Typography  *  * @section typography  */

一些文本编辑器,如 Textmate 或 CSSEdit,支持普通 CSS 注释中的@group 标记,以将 CSS 文件分成几个部分。CSSEdit 使用这些组来创建文档的可视轮廓;Textmate 需要一个捆绑包来实现此设置(有一些可用的)。虽然最新的草案提到了@group 标签,但是它还没有被合并到 CSSDOC 规范中。它允许 CSS 作者将选择器分组为有意义的部分,这些部分可以根据需要折叠和展开,从而更容易扫描和处理长 CSS 文件。有单独的包可供 Textmate 添加 CSSDOC 功能。 1

CSSDOC 标准还有更多的内容(足以写满一整章),所以我们建议看一下官方页面[cssdoc.net](http://cssdoc.net),以获得更深入的信息。

有一个合适的标准不仅使与其他 CSS 作者共享代码变得更简单,而且使从 CSS 文件中自动提取注释变得更容易,以便于文档化。

在这一节中,我们将参考和借用 CSSDOC 文档中包含的一些指导原则,主要是因为它们代表了对 CSS 注释标准化的展望,这对编写 CSS 的每个人都有好处。然而,我们认为,在某些情况下,CSS 元数据应该存在于文件本身之外(例如版本和作者),维护这样的注释很容易成为一项费力的任务,而不是一项有效的任务。评论一旦过时,就很快失去了价值。


1Adam Salter 出色的 GetBundles 捆绑包([github.com/adamsalter/GetBundles.tmbundle](https://github.com/adamsalter/GetBundles.tmbundle))使得定位、更新和安装 TextMate 捆绑包变得更加容易。

文件信息

CSS 注释最常见的用途是用于作者信息、创建和最后更新日期。CSS 文件通常在开头有这样的注释:

/* CSS file, created by John Doe, http://domain.com, on 12.28.2009 */

其他人会不厌其烦地添加更多信息,例如最后一个更新文件的人是谁,以及何时更新的:

/* CSS file, created by John Doe, http://domain.com, on 12.28.2009 Last update by: Jane Doe, on 5.19.2010 */

尽管我们认为有些数据最好存储在自动生成这些数据的版本控制系统中——CSS 作者会经常忘记更新这些数据,这使得这些数据变得毫无意义——但有些与样式表相关的信息可能应该存储在其中,并且很容易找到。

这是提及文件可能具有的任何依赖关系的好时机——例如,它是否使用主重置文件(CSS 重置文件在第四章的中有更深入的描述)或任何其他导入的样式表。它还可以说明文件是为哪个网站、子网站或页面创建的,以及它是否覆盖其他样式表。如果需要,还可以引用与该特定文件相关的命名空间实现。

下面是一个包含所有这些内容的示例:

`/*

Style sheet for: Igloo Refrigerator Parts Inc Christmas website

Created by John Doe, http://domain.com, on 12.28.2009

Importing reset file: /css/resets/master.css

Overriding: /css/base.css

Namespacing: use the “xmas” prefix for all the classes and IDs referenced in this file. For example: .xmasLatestNews or #xmasLatestNews

*/`

如果您使用的是 CSSDOC 类型的注释,文件信息区域(或文件注释)将如下所示(所有的描述和标签都是可选的):

/**  * Christmas theme  *  * CSS theme for the Christmas version of the Igloo Refrigerator Parts Inc website  *  * This theme has been developed by the Design Team at IRP Inc and should be used between  * the dates of November 7th and January 7th  *  * @project    IRP Christmas Site  * @author     Design Team at IRP Inc  * @copyright  2010 Igloo Refrigerator Parts Inc  * @cssdoc     version 1.0-pre  */

这些是数据片段的例子,对于开发人员来说,手边有这些数据片段是很有用的,可以避免为了访问它们而钻研版本控制系统。根据应用样式表的内容类型,可以添加许多其他细节。

这里主要关注的是这些信息必须保持相关和最新——它不能被轻易忽略,因为那样会违背这个评论块的目的。

目录

注释可用于在样式表的开头插入目录。这是有用的,因为它将消除滚动 CSS 文件(很容易有数千行)来找出它包含的内容以及在哪里找到特定信息的需要。

目录可以像下面这样简单:

`/* Table of contents:

1. Reusable classes
2. Structural elements
3. Colors and typography
4. Visual media
5. Widgets

*/`

也可以详细到如下:

`/* Table of contents:

Imports
Reusable classes
Structure
Navigation
Links
Typography
   Headings
   Body text
   Blockquotes
Lists
   Generic
   Definition lists
Forms
Images
Sidebars
Footers
Homepage
  Widgets

*/`

无论你选择哪种格式的目录,重要的是它要不断更新,以反映样式表的结构。

一些 CSS 编辑器会自动创建一个可视化的目录,例如,MacRabbit 流行的 CSSEdit(见图 2-5 )。

images

图 2-5。 CSSEdit 在左边创建 CSS 文件的自动轮廓([macrabbit.com/cssedit/](http://macrabbit.com/cssedit/) )

但是,如果软件不可用或者开发人员喜欢使用其他工具,在 CSS 文件的顶部包含一个手动更新的目录(即使很简洁)总是很有帮助的。

切片

只有当样式表实际上被分成几个部分时,目录才有意义。

当在 CSS 文件中创建部分时,您可能想让它们在代码的其余部分中易于识别。类似下面的划分不会很有特色:

`.myClassA {
   font-size: 14px;
}

.myClassB {
   font-size: 18px;
}

.myClassC {
   font-size: 24px;
}

/* Colors */

.error {
   color: red;
}

.success {
   color: green;
}`

相反,如果你使用这样的东西:

`.myClassA {
   font-size: 14px;
}

.myClassB {
   font-size: 18px;
}

.myClassC {
   font-size: 24px;
}

/* Colors
-------------------------------------------------------- */

.error {
   color: red;
}

.success {
   color: green;
}`

分部会更容易找到。

安迪·巴德在他出色的 CSS 掌握、、 2 、中提出,章节也应该易于搜索。如果我们将一个节命名为标题,在同一个文件中,我们有名为侧栏标题脚注标题的类,当使用内置的软件搜索工具时,我们可能必须通过几个不相关的结果才能找到我们想要的节标题。相反,如果我们在部分标题前添加一个不会在样式表的其余部分使用的字符,搜索过程将会更容易。安迪建议等号:

/* =Headings */

如果你搜索“=头”,软件很可能只会产生一个结果。

如果您使用的是 CSSDOC 注释,则一个节的划分格式如下:

/**  * Typography [optional]  *  * Description [optional]  *  * @section typography  */

不管你选择哪种风格,重要的是章节标题要与众不同,最重要的是,它们将样式表分成逻辑块。它们应该使浏览文档变得更容易,而不是更难;因此,创建的部分应该是直观的,即使它们遵循组织内的既定惯例。

当将一个样式表分成几个部分时,确保每页上重复出现的元素与特定于页面的元素分开也是一个好主意。

这将使查找冗余或不必要的选择器变得更容易——通过将它们隔离开来,在测试或完全删除它们时(也许将它们移到一个单独的样式表中)更容易注释掉它们。

您还可以考虑将 CSS 中经常使用的部分和依赖于交互的部分分开。例如,主标题将始终显示在页面上,但错误消息可能只在某些情况下显示。弄清楚这一点,以后就更容易找到过时的遗留代码。

色盘

CSS 文档顶部的另一条有用信息是对整个站点所用颜色的引用。

CSS 作者的一个常见错误是使用与应该使用的颜色相近的颜色。这可能是因为没有现成的品牌指南,或者即使有也不在手边,或者根本没有足够的时间去查阅。颜色最终是使用 Firebug、xScope 或 Photoshop 等工具选择的,这可能导致仅仅是近似的结果(特别是如果 CSS 开发人员获得的 jpegs 质量低于 100%,这种情况经常发生),从而导致在一个样式表中使用几十种相似但不同的颜色。尽管颜色错误对性能的影响很小,但随着时间的推移,颜色会越来越偏离其原始值。根据颜色的放置和使用,以及最终用户的显示器和显示设置,这些错误可能会变得更加明显。此外,如果使用一致的颜色,压缩时文件大小可能会更小,因为重复的文本模式可以实现更高效的压缩。


2

如果在 CSS 文件中添加一个颜色参考,就可以避免这些误解。使用样式表顶部的注释可以很容易地做到这一点:

`/* Color reference:

Main text      #111111
Headings       #999999
:link          #9f0000
:visited       #720000
:hover,
:active,
:focus         #004899

*/`

颜色参考可以而且应该比前一个例子更详细。它可以包含背景和边框颜色、更详细的印刷颜色、不同类型的链接颜色、根据站点部分的颜色等等。

需要记住的一件重要事情是,每个元素或元素组的描述应该是基本不可变的。如果我们使用“深灰色文本”而不是“主要文本”,那么引用表的目的就失去了。

这并不意味着这样的引用不应该被指定——当然有一个地方来说明网站应该使用的适当的红色或粉红色。但它们可能更多地被视为颜色参考中的次要元素,而不是主要的结构或印刷元素之一。

使用动态 CSS(在第九章中有更详细的解释)也可以减少不断参考颜色参考表的需要。

例如,当使用 LESS 时,可以在样式表的顶部声明如下所示的变量:

@mainText:   #111111; @headings:   #999999; @links:      #9f0000;

这将允许您在整个 CSS 文件中引用变量,而不是实际的颜色值,这也使更改颜色变得更加容易—您只需要在最初声明变量的地方做一次,它将更改颜色的所有实例,如下所示:

body {    color: @mainText; }

使用 CSSDOC 类型的注释时,使用@colordef 标记将颜色信息添加到 CSS 文件顶部的文件注释中:

/**  * Christmas theme  *  * CSS theme for the Christmas version of the Igloo Refrigerator Parts Inc website  *  * This theme has been developed by the Design Team at IRP Inc and should be used between  * the dates of November 7th and January 7th  *  * @project     IRP Christmas Site  * @author      Design Team at IRP Inc  * @copyright   2010 Igloo Refrigerator Parts Inc  * @cssdoc      version 1.0-pre  * ** * @colordef    #111111; main text** ** * @colordef    #999999; headings** ** * @colordef    #9f0000; links**  */

文件夹路径

注释可用于解释文件夹路径。这到底是什么意思?

尽管我们建议有一个清晰实用的文件夹结构(本书在第八章中对文件夹结构进行了更详细的介绍),但通常还是需要更深入一些——例如,为了更好地组织图片。发生这种情况时,在引用这些文件的样式表中引用这些路径是很有用的,这主要是为了避免最初没有编写 CSS 特定部分的开发人员犯错误。

CSS 文档顶部的这些注释可以像下面这样简单:

/* Branding elements are located in the Assets/Branding folder at the root of the main website */

只要记得保持它们的简洁和最新;否则,它们就没用了。

测量值

当创建复杂的 CSS 布局时,计算是不可避免的。

让我们描绘一个有两列的流体布局(见图 2-6 )。根据用户屏幕的大小,主要内容栏应该具有可变的宽度;侧边栏应该是 200 像素宽;我们还希望 20 像素垂直分隔两列。

images

图 2-6。简单的两栏流体布局:侧边栏宽度固定;主要内容区域具有可变的宽度。

我们的 CSS 可能看起来如下所示:

`#main {
   margin-left: 220px;
}

aside {
   width: 200px;
   float: left;
}`

在计算出初始度量值后更新 CSS 的人可能会发现很难理解所使用的一些值。

使用注释,我们可以编写一个更好的、有文档记录的解决方案:

#main {    margin-left: 220px; /* aside width + 20px of margin */ } aside {    width: 200px;    float: left; }

度量注释对印刷计算也很有用。

假设您有一个文本设置为 14 像素的页面,其line-height为 1.5。这意味着页面上的每一行的高度为 21 像素:14 × 1.5。

如果这个页面上的标题被设置为 18 像素,为了保持页面的垂直节奏不变,我们需要拿起计算器做一些数学计算——我们需要找到调整后的标题的line-height。这个思考过程是 CSS 注释的一个很好的候选:

h2 {    font-size: 18px;    line-height: 1.167; /* 21 (original line-height) / 18 (h2 font-size) */ }

即使不熟悉印刷概念的人可能不完全理解这种计算的需要,展示达到这种特定值所采取的步骤对于避免将来的错误是无价的。 3

images 注:这不是一本印刷书籍,所以我们不会过多讨论垂直间距是如何工作的或者应该如何计算的。关于网页排版的精彩介绍,请阅读 Richard Rutter 的“应用于网页的排版风格元素”,第 2.2.2 节[webtypography.net/Rhythm_and_Proportion/Vertical_Motion/2.2.2/](http://webtypography.net/Rhythm_and_Proportion/Vertical_Motion/2.2.2/)

记录黑客攻击和漏洞修复

黑客和 bug 修复文档是 CSS 注释的一些最重要的用途。

一个 CSS 作者经常会处理一个特殊的样式表,并且会遇到一个别人以前创建或编写的解决方案,这个解决方案看起来不清楚甚至不正确。也许可以使用一个更简单的解决方案,一个不会使 CSS 文件无效的解决方案,或者也许一开始就没有问题。

通常发生的情况是,即使代码不是最干净的,考虑到时间和预算限制、浏览器支持以及其他可能影响决策过程的因素,它可能是以前的开发人员能够编写的最佳解决方案。

在这些情况下,注释就派上了用场。例如,他们可以参考 Web 上的一篇特定文章,该文章解释了所采取的解决方案或进一步记录了错误:


没有向上或向下舍入的标准设置,所以浏览器不会始终如一地舍入包含小数位的值。

aside section {    float: left;    width: 50%;    display: inline; /* display: inline; fixes the Double Float Margin bug on IE5/6\. More on this bug/solution: http://www.positioniseverything.net/explorer/doubled-margin.html */ }

这种类型的文档注释应该简要地解释什么是 bug(如果它是一个众所周知的有文档记录的 bug,使用它的最常见的名称),采取的解决方案(提供一个链接,在那里进一步解释这个问题,如果存在的话),以及编辑或删除该特定黑客的后果。

当使用 CSSDOC 约定时,黑客和变通方法应该这样注释:

/**  * Name of the bug  *  * @workaround [or @bugfix]  * @affected   IE6  * @valid      yes [or no]  * @see        [include, for example, a link to an online article describing the bug]  */

记录 CSS 攻击和错误可能看起来很麻烦,特别是在处理众所周知和记录良好的 Internet Explorer 错误时,但是我们必须记住,即使我们中的一些人熟悉它们和它们的解决方案,许多开发人员并不熟悉。对我们的代码采取防御性的方法通常是最好的选择。

使用模板

拥有一个可供开发人员参考的 CSS 样式指南是有用和可取的,但是也建议为他们提供一个实际的 CSS 基本结构版本,这将使他们更容易理解应该如何组织一个或多个文件,以及在组织或团队中使用了哪些注释约定。

在此模板中,您应该包括以下内容:

  • 文档信息部分,包括文件依赖、覆盖、命名空间约定等等
  • 目录,包含最常见的部分
  • 调色板,它可以预先填充品牌颜色或只包括一个例子,它应该如何填充
  • 诸如“可重用类”、“结构”、“版式”等部分。

如前所述,诸如最后一个编辑文件的人和时间之类的信息应该用更合适和更可靠的方法来处理,如版本控制系统,以使过程自动化并避免重复劳动。应该鼓励开发人员在编辑这些文件时更新任何相关的部分。

类别和 ID 命名

在不深入了解什么是类和 id(我们假设您至少熟悉 CSS 的最基本概念)的情况下,我们将简要解释两者之间的区别。

类和 id 是在你的 HTML 中提供钩子的方法——也就是说,隔离和引用特定元素的方法。以下面的 HTML 片段为例:

`

  • Home
  • Contact Us
  • Search
`

没有简单的方法来引用“联系我们”列表项。我们可以使用第 n 个子元素选择器来定位无序列表元素的第二个子元素:

ul li:nth-child(2) {         … }

但这意味着,如果我们在列表项之前添加了任何列表项,我们的 CSS 将无法立即引用它(这是脆弱 CSS 的一个例子,在第四章中有所介绍)。这个选择器在我们可能要迎合的浏览器中也有不完全的浏览器支持。通过添加一个 ID,如下所示:

`

  • Home
  • Search
`

我们现在可以很容易地通过 CSS 定位这个元素,如下所示:

#contactLink {     … }

ID 是唯一的标识符。这意味着在任何一页中,它只能出现一次。从我们 HTML 的角度来看,每个页面都是一个独特的实体。因此,我们可以在整个站点中尽可能多地重用 id。在同一个页面上多次使用一个 ID 将导致不可预知的结果:可能只针对第一个项目,或者只针对最后一个项目,或者所有项目,或者根本不针对任何项目。它还会使我们的 HTML 无效(这会使调试和解析更加困难,并影响我们的搜索引擎排名)。一个元素最多只能有一个 ID。

那么,如果我们想针对多个项目,我们该怎么做呢?这正是上课的目的。在前面的例子中,如果我们想定位除了 Home 项之外的所有列表项,我们可以这样修改 HTML:

`

  • Home
`

我们现在可以很容易地锁定这两个列表项:

.nonHomeLink {     … }

元素可以有任意多的类,如果使用多个类,应该用空格分隔。如果我们想用多个类作为目标元素,并使每个类都是强制性的,我们可以像这样将类链接在一起:

.warning.error {     … }

这将针对具有“警告”类和“错误”类的任何元素但是,请注意,Internet Explorer (IE) 6 和早期版本不理解使用多个类的选择器。在前面的例子中,IE 6 和 5 将只读取最后一个类“error”,这可能会引起很多人的头痛。避免问题的最简单方法是避免在选择器中使用链式类,而是单独声明每个类,确保它们相互补充。IE 6 将解释正确定义了多个类的 HTML,但不是多个类选择器。

尽管命名元素的类和 id 的确切方式并不重要,但是有几件事您应该记住:

  • 语义学
  • 接受的字符
  • 惯例
  • 一致性

语义学

为你的 CSS 命名钩子的方式应该是语义化的和描述性的,而不是描述性的。以下是一些错误 ID 和类名的示例:

  • 紫色框
  • 大头
  • 左上框
  • 下划线和绿色

更合适的是以下内容:

  • mainNavigationItem
  • 出错信息
  • 外部连接
  • 侧面内容

W3C 网站上有一个关于这个主题的页面,其中解释道“人们经常使用 bluetext 或 redborder 这样的类名。命名类的一个更好的方法是使用该类的某个 HTML 元素所具有的角色。你可以在[www.w3.org/QA/Tips/goodclassnames](http://www.w3.org/QA/Tips/goodclassnames)了解更多信息。

使用非代表性的类名和 ID 名将使它们可重用并适应变化。例如,如果您的页脚的背景颜色是紫色,并且容器的类名是“紫色”,那么当品牌更新并且新的公司徽标是橙色时,会发生什么情况?类名“紫色”变得过时且不连贯——徽标是橙色的!

这就是为什么选择与内容相关的类名和 ID 名很重要,因此,不要将侧边栏命名为“boxLeft”,而应该命名为“sidebar”——您不知道下周它是否会移到右边。或者,与其将红色警告框命名为“红色”,不如将其命名为“警告”,这样,如果有一天它变成黄色,这个名称仍然有意义。

然而,在某些情况下,使用表示性类名可能比使用结构性类名更灵活。例如,如果在网站上不止一个实例中使用了某种类型的容器框(如带有边框的容器),使用“bordered”这样的表示名称可能是最聪明的解决方案,使其可重用,而不是将特定的容器样式绑定到某种内容类型(创建冗余并使文件变大)。

但是,我们并不鼓励在 CSS 文件中到处使用表示性的类和 ID 名称——有时最好的解决方案并不是最干净或最漂亮的,我们需要转向一种更通用的思维框架。

公认的人物

W3C 指定了哪些字符和组合在[www.w3.org/TR/CSS21/grammar.html#scanner](http://www.w3.org/TR/CSS21/grammar.html#scanner)对于类和 id 是有效的。本质上,类名或 ID 必须以下划线、连字符或字母开头。第二个字符可以是下划线或字母,后面可以跟任意数量的下划线、连字符、字母或数字。您还可以包含转义的 Unicode 字符。

但是,在使用类名和 ID 名时,应该避免使用某些字符和组合。其中包括:

  • 根据 HTML 标准,下划线在 HTML 属性中一度是合法的,但根据 CSS 标准却不是。在一些浏览器中,实现了各种方法来转义下划线,并取得了不同程度的成功。一些较旧的浏览器(包括 Netscape 4.x 和 Opera 3.5x)在类名或 ID 名中误解了下划线,因此尽管您不太可能支持这些浏览器,但避免使用这些字符似乎是一种好的做法。
  • 正斜杠或反斜杠:这些是不被接受的字符。
  • 星号:这些是不被接受的字符。
  • 以数字字符开头的类名或 ID 名:一些浏览器会接受这些,但很多不会。
  • 以连字符开始的类名或 id:一些浏览器会接受这些,但是很多不会。

有很多方法可以对这些字符进行转义;例如:

为了使“. 55 英尺”成为有效的类,CSS2 要求对第一个数字进行转义(“。“35 英尺 5 英寸”)

[www.w3.org/TR/CSS2/grammar.html](http://www.w3.org/TR/CSS2/grammar.html)

这并不简单,因为不同的浏览器可能会错误地解释边缘情况。最好只是避免使用这些字符。您还应该记住,ID 和类名是区分大小写的。

正如您所看到的,对更难理解的字符的支持各不相同,如果您选择使用不符合最简单规则的字符或命名约定,并且不太可能经过很好的测试,新浏览器中可能会出现错误。要遵守的最佳规则是始终只使用字母数字字符或连字符,并且只以字母字符开始类名或 ID 名。您应该避免任何重音或 Unicode 字符。您可以安全地使用连字符,但这似乎是对单个字符的浪费,当您考虑足够大的带宽时,这一点很重要——尽管它们确实有助于获得更易读的值。

习俗

尽管对于我们应该如何命名我们的类和 id 并没有什么规则,但是一些约定现在已经相当确定了,并且发现无处不在的命名实践是很常见的。

这些是您可能在大多数样式表中看到的一些类和 id 的示例:

  • 。标题/#标题
  • 。页脚/#页脚
  • 。侧栏/#侧栏
  • 。导航/#导航

在开发新的 HTML 5 元素时,这些名称的普遍存在使它们成为显而易见的选择(有一些变化):headerfooterasidenav。事实上,现在决定不跳到 HTML5 的作者被鼓励使用新元素名作为更成熟元素的类名或 ID 名,比如divul

有许多任何人都能理解的类名和 ID 名,即使不熟悉手边的特定样式表。一些例子是标题、导航、侧边栏、页脚、主页面、包装、内容、列、提交、错误、警告、发布和动作。

如果可能的话,我们建议使用这些容易理解的名称,而不是更晦涩的名称。这将使代码更容易预测,因此对于没有创建初始命名的人来说也更容易理解。

在组织内部,可能存在其他不为 it 部门所知的命名约定。例如,当命名空间 CSS 选择器(本章稍后将详细介绍命名空间)时,应该有一个内部约定,这样就不会落入每个 CSS 作者的个人偏好中。

合理的命名和明确定义的内部指导原则的结合应该产生一致和直观的类名和 ID 名。

此外,使用可预测的类名和 id 可以帮助一些屏幕阅读器或解析器,并提高 SEO。微格式是一组标准化的规范,用来描述人、日历、产品评论等等。使用这些将有助于搜索引擎理解您的内容,并在搜索结果中以更具吸引力的方式呈现这些内容,以及许多其他好处。在[microformats.org/](http://microformats.org/)阅读更多关于微格式的内容。

案例

使用语义类名/id 并遵守我们的可接受字符列表的结果仍然会产生大量潜在的可接受的类和 id,我们可以将它们用于我们的元素。以下所有内容都是完全有效的类名:

  • 主要内容
  • 英雄形象
  • 初级导航

这里最重要的是我们保持一致,记录我们期望我们的开发人员使用的 ID 或类名作为参考。在可能的情况下,为了简单起见,尽量不要将单词串在一起是有意义的。然而,当这是不可能的时候,有两个流行的关于你应该如何命名你的类和 id 的思想流派。

骆驼案

一个流行的命名约定叫做骆驼案。骆驼大小写是一种习惯,总是以小写字母开头,去掉所有空格和标点符号,并使每个连续的单词以大写字母开头。例如:

  • 主要内容
  • 英雄形象
  • 初级导航

使用首字母缩略词时,应该以相同的大小写将它们组合在一起。如果在名字的开头,让它们都小写;否则,请将它们全部大写,如下例所示:

  • pdfLink
  • 链接到 pdf

images 注意: JavaScript 实际上在如何使用 camel case 上是内部不一致的。一个常用的方法是 getElementById(),一个常用的属性是 innerHTML。我们建议我们使用的方法更清晰,但一如既往,选择你喜欢的方法并保持一致。

Camel case 是 IT 行业中一个很好理解的术语,它不添加不必要的字符,也不影响语义。最重要的是,新开发人员很可能已经知道了,并且很容易简洁地解释。通过不使用任何其他字符,我们可以使用这些字符作为名称空间的明确分隔符(参见本章后面的内容),并节省使用的字符数量。

连字符

使用 camel case 存在缺陷。如果开发人员不是 JavaScript 开发人员,他们可能不习惯这种方法,可能会发现如果没有某种分隔符,很难阅读或浏览。CSS 是一种连字符语法(background-colorborder-radius等等),许多人觉得只使用一种语法更舒服,尽管其他人认为视觉上的区别很有用。有些人还认为 HTML 是一种典型的小写语言,在 camel case 中,不匹配的大小写更容易出错。类名和 ID 名是区分大小写的,所以说“mainContent”和“maincontent”是不一样的,坚持小写更容易。用连字符连接的类和 id 如下所示:

  • 主要内容
  • 英雄形象
  • 初级导航

这种约定更容易理解,并且还有其他性能优势。坚持小写字符减少了你的字符集,并有可能增加重复模式,这将使你的压缩算法更有效。但是,在编辑器中查看文件时,断字会增加额外的字符,并留下较少的水平空间。

在决定遵循哪种约定时,要记住的重要事情是,采用这种约定的最有价值的结果是它确保并促进了一致性,使开发人员更容易想出其他人都能理解的新名称。在本书中,我们将使用 camel case 作为例子,但是您应该使用您认为更合适的例子。

命名空间

命名空间是防御性 CSS 的技术之一。通过隔离部分代码,我们尽最大努力确保它们不会以任何方式影响我们自己或他人开发的现有和未来的代码,也不会受到未来编写的代码的影响。

我们来看一个例子。假设有一个侧边栏小部件,它列出了您公司博客的最新帖子。这个小部件可以插入公司的任何站点或小型站点,甚至可以插入外部站点(您可以鼓励公司员工将其添加到个人博客中,或者鼓励供应商将其添加到他们的公司站点中)。为这个小部件及其类和 id 创建 CSS 时,最终结果可能是这样的:

`

Latest posts from the company blog

`

这一切看起来都很好,但是如果代码被注入到一个有另一个名为“blog”的类的页面中(这听起来不是完全不可能),会发生什么呢?代码在自找麻烦。

CSS 中命名空间的目的是避免这些类和 id 的冲突。通过向类和 id 添加前缀,我们将它们与现有的和即将出现的其他类和 id 分开,否则它们会干扰我们的代码(或受其影响)。

在前一种情况下,我们可以使用以下代码,其中前缀“irpb-”代表 Igloo 冰箱部件博客(我们虚构的组织的名称):

`

Latest posts from the company blog

`

现在这个代码片段已经被隔离在它自己的(模拟的)名称空间中。另一个网站使用相同的类前缀是不太可能的,而且,如果所有团队成员都知道您的组织内提出新名称空间的惯例,那么也不应该与内部网站有任何冲突。作为分隔符的连字符充当一个清晰的视觉指示器,表明“irpb”是一个名称空间。

命名空间使 CSS 更加模块化;它允许我们将可转移的代码片段插入到不同的地方,无论是在团队必须工作的网站范围之内还是之外。如果你的网站包含第三方的内容,比如小工具或者广告,命名空间可以保证他们的代码可以很好地与你的兼容。

WordPress 是一个为一切事物命名的公司的好例子。WordPress 数据库中的表格被称为wp_userswp_optionswp_posts,等等。推荐插件开发者使用他们插件的名字作为他们插件中任何类的名称空间。这里有一个例子:

`

Share this Article:
            
  • Print
`

这段代码有一些非常糟糕的错误(见下面的注释),但是忽略它们,下面是我们可以看到的类:

  • 好交际的
  • 社交 _ 标语
  • 社交电话
  • 社交活动

这个名称空间显然是“sociable”,所以只要我们页面上没有其他类以 sociable 开头,就不会有冲突。

images 注:四种不同的类名约定!四个!显然,四个不同的开发者开发了这个插件(或者一个非常不一致的一个)。

可重用类

正如您到目前为止所看到的,命名空间的一个缺点是需要适当的文档。它通常使用不熟悉代码的人不能立即识别的类和 ID 前缀——通常是缩写或首字母缩略词——所以它们的含义应该解释,要么在实际 CSS 文件的顶部,要么更好的是,在公司的 wiki 中。

因为命名空间一定会被重复创建,所以定义 CSS 作者在生成更多案例时应该遵循的适当约定也是一个好主意。

尽管我们可能已经决定使用 camel case 来命名我们的 id 和类,但是我们可能希望在我们作为前缀的名称空间之后包含一个连字符。这直观地证明了前缀是一个名称空间,并且使得这些类或 id 与其他任何东西冲突的可能性更小。一些例子如下:

  • irpb 主内容
  • irpb-heroImage
  • irpb-初级导航

这真的是我们所能做的最好的防御。可能我们只是想根据页面或页面类型以不同的方式对待页面。为了实现这一点,另一种常用的命名空间技术是给body(或html)元素自己的类或 ID:

<body class="homepage">  

这将更容易根据元素所在的页面来设计不同的样式,这也是很多内容管理系统默认添加到body元素中的东西。

在这种情况下,如果我们想改变主页页脚的样式,甚至完全隐藏它,我们可以使用赋予 body 的类:

`footer {
   display: block;
}

.homepage footer {
   display: none;
}`

虽然你有很多规则,但这会使你的 CSS 非常冗长。记住,给身体一个 ID 或类会使使用那个 ID 或类的选择器更具体(更多关于具体性的内容在第三章)。

CSS 命名空间实际上不是严格的命名空间,不像在其他语言如 XML 中那样。相反,它是对它的模仿。在 XML 中,我们可以在特定的名称空间中定义标记组,当我们包含这些标记时,我们在它们前面加上名称空间和冒号,就像这样:

*<namespace:tagname />*

在 XML 中,这种命名约定是功能性的,也是必需的。在 CSS 中,它是我们可以选择实现的工具,帮助我们保护和分类代码。

CSS 名称空间模块

尽管它主要适用于 XML,但不太适用于 HTML,值得一提的是,在撰写本文时(??),CSS3 命名空间模块正处于 W3C 的候选推荐阶段。目前所有现代浏览器都支持@namespace 声明,Internet Explorer 的下一个版本 IE 9 也应该支持它。

使用@namespace 规则,您可以将命名空间前缀声明为以下示例(前缀区分大小写):

@namespace irpb "http://example.com/irpb"  

在前面的例子中,“irpb”是用来表示名称空间的前缀(别名),而“example.com/irpb”是名称空间的完整表示,它在页面上应该是唯一的(这也是经常使用 URL 的原因)。要在 CSS 选择器中调用名称空间,需要使用带有竖线(|)的类型选择器。选择器中位于栏之前的部分是名称空间前缀。如果在类型选择器中没有声明名称空间,则该规则适用于没有名称空间的元素;如果使用星号,则该规则适用于属于任何命名空间的元素:

irpb|div {    … } |div {    … } *|div {    … }

命名空间规则必须在@charset 和@import 规则之后声明,并且必须在所有其他 at 规则和普通规则之前声明。它仅适用于该特定样式表(不适用于导入的样式表)。我们出于兴趣将它包含在这里,并不建议您在生产代码中使用它。

总结

这一章强调了拥有一套明确的指导方针的重要性,团队或组织中的 CSS 作者可以遵循这些指导方针来创建更加标准化和文档化更好的代码。这些指南应该有助于创建一个完整的 CSS 样式指南,可以由任何在创建和使用样式表方面有任何输入或影响的人共享和更新。

虽然我们并不声称对 CSS 格式和最佳实践有确定的答案,但我们相信本章中的建议是新开发人员和有经验的开发人员最广泛观察和接受的,这反过来又使它们更容易转化为一种编码标准,这种标准会受到欢迎并用于在您的团队中促进一致和有效的代码。

在下一章中,我们将看看 CSS 的基础,它是如何工作的,以及它的特点。通过利用 CSS 的特性和避免常见问题,这些将使你和你的团队能够以更有效的方式使用 CSS。

三、基本原则

在使用 CSS 的时候,有一些基本的概念是开发人员应该注意和理解的,但是这些概念往往会被忽视甚至忽略。深入了解这些概念将意味着创建的 CSS 将得到更好的规划,错误将更容易避免,并且样式表通常会更简单、更干净、更容易维护。

CSS 通常被认为是一种理解起来相对简单的语言(它的语法很直观,很容易理解,即使对于从未写过一行 CSS 的人来说也是如此)。但是 CSS 作者需要一些经验来编写复杂的 CSS 布局,在不太兼容的浏览器中不会崩溃,同时保持他们的样式表干净和灵活。

在一个理想的世界中,我们网站的所有访问者都将使用最新的浏览器,我们不需要在我们的 CSS 文档中加入黑客、条件注释和变通方法:一切都可以在任何地方工作。我们并不是生活在一个理想的世界中,因为我们必须处理各种各样的浏览器不一致,我们的 CSS 文档最终看起来与我们希望的相差甚远。处理高流量网站的一个结果是,我们的访问者将使用各种各样的浏览器,从最新的测试版模糊浏览器到旧版本的 IE 浏览器。

images 注意:你的流量越大,来自更不知名设备的访问的可能性就越大。你的公司应该在内部讨论哪些是你支持的,哪些是你不支持的,但重要的是要明白(例如)如果只有 0.5%的用户使用 IE 5,而你的网站每天收到 2000 万次点击,那么你的网站每天将收到来自使用 IE 5 的用户的 10 万次点击!这不是一个微不足道的数字,应该仔细考虑。

重要的是将指导方针付诸实践,包括如何最好地处理这些情况,并确保团队成员熟悉 CSS 的基本工作原理。采取这一步骤可能意味着新员工需要更长的适应期,但你应该认识到,指南永远不会涵盖所有可能的情况或浏览器错误,编码人员每天都要做出决定,这些决定将影响网站的有效性和性能。你的团队中的这种额外的培训和知识只能是一件积极的事情,使团队能够更好地理解 CSS 和编写好的代码,并且它将在光环效应中向外共享。

在本章中,您将:

  • 了解重要的 CSS 概念,如级联、重要性、继承和特异性
  • 理解编码的重要性以及如何实现本地化
  • 了解如何处理特定于浏览器的 CSS
  • 了解对付黑客的最佳方法
  • 发现服务器端用户代理检测是否是一个好主意
  • 看看浏览器不一致的例子,以及如何最好地处理它们

级联:起源、重要性和继承

了解级联如何工作是构建灵活和健壮的样式表的基础。如果忽略这些基本概念,或者只是在需要的时候临时添加一些规则,而不考虑它们之间的相互影响,那么你的网站的 CSS 就会因为每一次添加而成倍地复杂化。您将生成更大的文件,增加冗余,并使开发人员更难理解或进一步扩充它们。对 CSS 工作原理的深刻理解将使你和你的团队能够做出最好的决定,以使样式表更容易维护。

“层叠”的概念是 CSS 的核心。正是这个过程决定了哪些 CSS 规则将影响哪些元素,并赋予每个规则特定的权重和优先级。当所有因素都考虑在内时,在多个规则适用于特定元素的情况下,权重较大的规则将优先于权重较小的规则。

为了给规则赋予权重,级联依赖于三个方面:重要性、继承性和特异性。如果针对同一元素的两个或多个规则最终具有相同的权重,则最后一个规则将胜出。这同样适用于在同一规则中重复的属性(最后定义的属性值是应用的属性值)。

在这一部分,我们将讨论起源、重要性和继承;而特异性足够复杂,以至于后来有了自己独立的部分。

起源和重要性

来自三个不同来源的样式表可以影响网页:

  • 用户代理样式表
  • 用户样式表
  • 创作样式表

用户代理样式表(见图 3-1 )是那些默认出现在用户代理(用户用来访问我们代码的设备,通常是网络浏览器)中的样式表。即使当我们打开一个没有链接 CSS 文件的 HTML 文件时,它看起来也是没有样式的;实际上,它是由用户代理样式表进行样式化的。这个样式表为未访问的链接添加典型的蓝色,为已访问的链接添加紫色,为不同类型的标题添加不同的字体大小,等等。

images

图 3-1。bbc.co.uk 的主页,仅应用了用户代理样式表(在本例中是 Firefox 的样式表)。

例如,用户样式表(见图 3-2 )可以是基于用户浏览器偏好应用于页面的样式表,如更大的字体或特定的字体系列,以便于阅读。

images

图 3-2。应用了用户样式表的维基百科首页:在这种情况下,用户样式表确保任何网站上的文本都不会小于 18px。

用户样式表也可以是用户创建的 CSS 文件,浏览器将读取这些文件以更改特定站点的设计或细节。在这种情况下,外部文件通常存储在用户的机器上,并由浏览器引用。

默认情况下,作者样式表覆盖用户样式表,用户样式表又覆盖用户代理样式表。但是,有一种方法可以让用户样式表更有分量,并覆盖作者声明。这可以通过使用!important声明来实现,它确保了无论发生什么,用户总是对页面如何显示有最终决定权。

!important声明是在逐个属性的基础上使用的,它可以应用于内联、嵌入或外部样式表:

p {    color: black !important; } p {    color: red; }

在前面的例子中,因为我们使用了!important声明,所以第一个规则将是应用于p元素的规则,即使第二个规则在后面出现并且具有相同的特异性(关于如何计算选择器特异性的更多细节,请参见下一节)。 1

在作者样式表中应该小心使用,如果可能的话应该避免使用,因为它会干扰规则的特殊性,为过于具体和复杂的 CSS 让路。

让我们看看下面的例子,用户在网站上隐藏了一个广告栏:

#myfavoritesite .advertbanner {    display: none !important; }

带有!important声明的用户样式表将总是覆盖来自任何其他来源的样式表,所以在这种情况下,用户可以确定广告栏不会被显示。

为了让用户更容易将自己的 CSS 规则注入特定的网站,一些开发人员在他们的页面的body元素中添加了一个 ID,甚至更进一步,既添加了站点范围的 ID,又添加了逐页类,这样用户就可以在需要时轻松地指向特定的页面。

作者样式表是由 CSS 作者创建的,在服务器返回的实际页面中提供。它们可以内嵌在标记中(使用 HTML style 属性),嵌入在页面中(使用 HTML style 标记),或者外部链接(使用 HTML link 标记或@import 规则)。

在混合添加了!important之后,我们剩下以下不同类型的声明,从最重要到最不重要:

  • 标记为重要的用户声明
  • 标记为重要的作者声明
  • 作者声明
  • 用户声明
  • 用户代理声明

继承

W3C 对继承的定义指出“继承是将属性值从父元素传播到其子元素的一种方式”([www.w3.org/TR/css3-cascade/](http://www.w3.org/TR/css3-cascade/))。

这意味着一些 CSS 属性在默认情况下(或者可以被强制)可以被子元素继承。


1 在这个例子中,我们在!important前加了一个空格,以便于阅读。空格是不必要的,这是你在缩小文件或编写 CSS 格式指南时需要知道的。

让我们来看一个典型的场景:

body {    color: #111111;    padding: 20px; }

在前面的例子中,color属性将被body元素的子元素所继承,因此标题、段落、列表和其他文本都将是深灰色(“#111111”)。然而,padding属性不会被继承;如果我们将一个div或一个段落放在body元素中,它们将不会继承 20 像素的填充。

这些是遗传如何工作的例子。在第一种情况下,颜色,它影响父元素的子元素是有意义的;除了生成大得多的文件之外,还必须声明父元素中每个元素的颜色,这将是令人厌烦和低效的。另一方面,如果它的子元素继承了 20 个像素的填充,那么重写这些填充也是令人厌烦的。

W3C 在其网站上有一个所有 CSS2.1 属性的列表,[www.w3.org/TR/CSS21/propidx.html](http://www.w3.org/TR/CSS21/propidx.html),其中说明了它们是否被默认继承,初始值(默认值)和可能值,以及其他特征。

继承对于默认继承的元素是有意义的,但是也可以通过使用“inherit”值对不继承的元素进行强制继承。

假设您希望aside父元素中的section元素继承其margin值:

aside {    margin: 10px; } aside section {    margin: inherit; }

使用这个 CSS,aside元素中的每个section元素现在将有 10 个像素的边距。

W3C 规范中解释了用户代理处理继承的方式:

属性的最终值是一个四步计算的结果:该值通过规范确定(“指定值”),然后解析为用于继承的值(“计算值”),然后在必要时转换为绝对值(“使用值”),最后根据当地环境的限制进行转换(“实际值”)“([www.w3.org/TR/CSS2/cascade.html#value-stages](http://www.w3.org/TR/CSS2/cascade.html#value-stages))。

根据规范,级联按以下顺序查找四个值,以确定属性的最终值:

  1. 指定值:用户代理确定属性的值是来自样式表,还是继承而来,还是应该取其初始值(即属性规范中规定的值)。
  2. 计算值:指定的值被解析为计算值,即使某个属性不适用也存在。在这个阶段,不必对文档进行概述。
  3. 已用值:已用值取计算值,并解析任何依赖关系,这些依赖关系只能在绘制文档后才能计算(例如,百分比)。
  4. 实际值:这是在应用任何近似值(例如,将小数值四舍五入为整数)后,用于最终渲染的值。

计算继承时使用的值是计算值。即使 CSS 中没有声明该属性,该值也存在,这使得即使父元素没有指定该属性,也可以强制继承。

让我们看看下面的例子:

HTML:

`
   

To find what you are looking for visit Google.

`

CSS:

body {    font-family: Arial, sans-serif;    font-size: 12px; } a:link {    color: inherit; }

如果我们没有为 link 元素(“a”)指定颜色,我们将会从浏览器(用户代理)样式表中看到默认的蓝色。虽然我们没有为body元素指定文本颜色,但是当使用未访问链接选择器的颜色属性的“inherit”值时(“a:link”),它将使用父元素的计算值。在这种情况下,这是初始值,其颜色属性取决于用户代理(如 W3C 的属性规范中所述),但往往是#000000(黑色)。

“inherit”值并不常用,但是在上面的例子中,当子属性依赖于父属性时,创建依赖关系比显式声明一个值更容易,这种情况下它会很有帮助。

“inherit”值在实践中很少使用的原因之一是,除了“direction”和“visibility”属性之外,它不被版本 8 之前的 IE 版本支持。当使用它的优势(例如,当它增加了更大的灵活性,使 CSS 更新更快,更不容易出错)取代了使用它时可能出现的任何呈现差异时,应该使用它。

与“inherit”关键字相对的是 CSS3 中引入的“initial”关键字。此值允许您通过指定该属性的初始值(如属性规范中所述)而不是继承的值来取消继承。然而,只有 Firefox(使用-moz-initial关键字)和 WebKit 支持这个属性。

继承和万能选择器

通用选择器(" * ")匹配文档树中的任何单个元素(注意这里使用了单词 single ,因为它不会匹配两个或更多元素的组合,或者零个元素)。如果使用不当,它会破坏继承性,很容易将样式表变成继承性和特异性的噩梦。

举以下例子:

HTML:

`


   

      

         

Main title


         

Secondary title


      

   

        …

`

CSS:

section header * {    color: red; } section header h2 {    color: black; }

第一个 CSS 规则包括通用选择器,它针对的是header元素中的所有元素,无论是直接的子元素、孙元素还是更深层次的元素。对于第二个 CSS 规则,预期的行为是h2中的任何元素都应该是黑色的。但是,如果我们在h2中有另一个元素,会发生什么呢?因为最初的规则是针对 header 中的任何元素,所以它将与下面的语句相同:

section header h2 i {    color: red; }

因为这个选择器比“section header h2”更具体,所以最终结果会将单词“Secondary”呈现为黑色,而将“title”呈现为红色(参见图 3-3 )。

images

图 3-3。使用通用选择器时继承被破坏

虽然,起初,在这种情况下使用 CSS 通用选择器似乎是确保每个元素都具有所需颜色的最安全的选择,节省了宝贵的代码行,但这并不是健壮 CSS 的一个例子。相反,它是一个例子,说明破坏继承会导致不必要的复杂、不可预测和冗长的样式表——这正是我们在一个高流量网站中想要避免的,在这个网站中,许多人可能在处理许多文件。

特异性

在使用 CSS 时,需要仔细考虑和计划特殊性,在处理频繁覆盖和导入样式表的大型 CSS 文件时,这一点更为真实,因为在高流量网站中往往会发生这种情况。

使用更通用的选择器开始开发 CSS 文件是一个很好的经验法则,在适当的时候增加特异性。反过来工作要困难得多,并且总是会导致无法重用的过于具体的选择器,以及不必要的冗长和不灵活的样式表。

依赖于选择器的顺序会使你创建的样式表更加脆弱(这个主题在第四章中有进一步的阐述)并且会导致不必要的冗余。当您需要覆盖一个规则时,您将在文件中创建一个新的规则,这将发生几次,直到您一次又一次地重复同样的事情。如果由于某种原因改变了顺序,您希望应用于元素的属性(过去是最后应用的)将不再被应用,因为它们依赖于选择器的顺序。依赖特定性而不是选择器的顺序将使样式表更容易编辑、维护和重构,并且更健壮。

选择器的特殊性也会对网站的性能产生影响,因为选择器的某些部分是从右到左进行非直观评估的,而更具体/复杂的选择器在查询 DOM(文档对象模型)时会遇到更多的问题。你可以在第八章中读到更多这方面的内容。

无论你是否需要在更深的层次上使用高度特定的规则,这最终都是你的决定,也是你需要你的 CSS 有多模块化和灵活的副产品,但是在你的团队计划样式表的方式中,特定性应该是一个基本的考虑。

计算特异性

在为 CSS 规则分配权重时,cascade 将首先根据它们的重要性和来源对它们进行排序(如前一节所示)。如果规则具有相同的重要性和来源,那么它们将根据其具体性来排列优先级:更具体的选择器将覆盖不太具体的选择器。最后,如果两个选择器具有相同的来源、重要性和特异性,样式表中后面的那个将优先于前面的那个。这也适用于单个属性,因此如果在同一个规则中多次声明同一个属性,则最后一个声明将覆盖之前的声明。

因为导入的样式表(使用@import声明)必须在其他规则之前指定,所以当 CSS 文件的其余部分中有其他具有相同权重的未导入规则时,导入的规则将被覆盖(由于选择器的顺序,它们的优先级较低)。

为了计算特异性,根据 W3C 规范([www.w3.org/TR/css3-selectors/#specificity](http://www.w3.org/TR/css3-selectors/#specificity)),我们使用重要性递减的四种数字表示法(a、b、c 和 d ),其中

  • 如果声明在 style 属性内,则等于 1;0 如果不是
  • b 等于 ID 选择器的数量
  • c 等于属性选择器、类和伪类的数量
  • d 等于元素名和伪元素的数量

非 CSS 表示标记,比如font属性,将被赋予 0 的特性。

基于此列表,以下选择器的特异性为 1,0,0,0 (a=1,b=0,c=0,d=0):

<section style="padding-bottom: 10px;">

因为是内联 CSS,“a”等于 1,其余数字等于 0。请记住,即使链接到而不是内联的规则中有 10 个 ID 选择器(因此具有 0,10,0,0 的特异性),它的优先级仍然低于上面的选择器——特异性在其计算中不使用十进制(或十进制)系统,而是使用无限进制:如果“a”等于 1,规则将始终优先于它等于 0 的规则。

这个更复杂的选择器将具有 0,0,1,3 (a=0,b=0,c=1,d=3)的特异性:

article section h1.title {    … }

由于不是内联的,“a”等于 0,“b”等于 0,因为没有 id,“c”等于 1,因为它有一个类选择器,“d”等于 3,因为它有三个元素选择器。

根据所提供的列表计算特异性似乎令人望而生畏,但事实是,根据一些经验,很容易看到一个选择器,并判断它是否比另一个选择器更具体或更不具体(也许它有一个或两个 ID 选择器或其他类似的明显提示)。在编写 CSS 时,您应该注意不要在不必要的地方创建高度特定的选择器,每当出现更棘手的情况时,Firebug 或 Safari Web Inspector 等工具会帮助您理解特定性是如何应用的,它们会按特定性的顺序向您显示规则(更特定的规则在顶部),并删除被更特定的规则覆盖的属性。你可以在第十章中读到更多关于这些的内容。不过,有两件重要的事情需要记住:内联 CSS 比嵌入或链接 CSS 具有更高的优先级,一个 ID 选择器将胜过任何数量的类、属性或元素选择器。

该!重要声明

在速记属性上使用!important声明等同于将每个子属性重新声明为重要属性(即使这意味着它们被恢复到它们的初始(缺省)值)。

例如,假设我们有以下选择器:

h1 {    font-family: Georgia, serif;    font-size: 18px;    font-style: italic; }

稍后在样式表中我们声明如下:

h1 {    font: 18px Arial, sans-serif !important; }

结果将与以下内容相同:

h1 {    font-style: normal !important;    font-variant: normal !important;    font-weight: normal !important;    font-size: 18px !important;    line-height: normal !important;    font-family: Arial, sans-serif !important; }

发生这种情况是因为没有在!important声明中明确定义的属性(在这种情况下,font-stylefont-variantfont-weightline-height)被恢复到它们的初始值(在属性的规范中指明),即使它们是在一个不太明确的规则中声明的(比如在这种情况下的font-style: italic)。

命名空间和特异性

在某些情况下,高度特定的选择器是必要的。例如,当创建要在各种页面、子网站、小型网站甚至第三方网站上使用的小部件或代码片段时,通常的做法是命名代码的这一部分,这在 CSS 中基本上意味着使用特定的类、ID 或类或 ID 的前缀来隔离它(我们在第二章中更详细地讨论了命名空间)。为了使页面具有不同的样式,命名整个页面也很常见;在这种情况下,我们将向 body 元素添加一个 ID 或类,例如:

<body class="home">

重要的是要认识到,虽然这是为不同页面或页面上的不同部分创建不同样式的一种常见且简单的方法,但它会影响特殊性。当在我们的 CSS 中定位这些页面或代码片段时,我们必须为规则引入一个类或一个 ID,增加它的特异性,从而使它更难被覆盖。例如,要使页面中我们指定为“home”类的h2标题比站点中的其他标题大,我们可以使用以下规则:

h2 {    font-size: 24px; } .home h2 {    font-size: 36px; }

在前面的示例中,即使没有类,规则也将被应用,因为规则将具有相同的特性,但将在以后声明。然而,这将依赖于顺序,这将适得其反,因为如果规则的顺序被改变,它将失败(你可以在第四章中了解更多)。

如果出于某种原因,我们需要在主页中再次覆盖此设置,例如,我们需要向特定标题添加一个类别,并创建一个更具体的规则:

.home h2.highlight {    font-size: 30px; }

这会产生一个过于具体的规则的雪球,不可避免地导致不必要的复杂 CSS。这里的诀窍是仔细计划这些情况,这些情况会更频繁地发生,因为整个网站需要更多的某些元素和设计的变化,如果没有设计库,或者有但不经常更新,情况会更糟(你可以在第五章中阅读更多关于设计库和保持设计一致性的内容)。你应该准备一个适应性强的样式表,以及一套关于特定开发人员如何应对特殊情况的指导方针——不断重写高度特定的选择器不利于创建灵活的 CSS,但是当类和 id 是更高效和健壮的解决方案时,避免使用它们也不是高流量网站的选择。

使用你的工具

使用诸如 Firebug(见图 3-4 )或 Safari 的 Web Inspector(见图 3-5 )这样的工具,可以更容易地理解哪些属性优先,哪些属性优先。这些工具还可以显示用户代理样式表和计算值,即使它们没有在 CSS 中声明。

images

图 3-4。使用火狐的 Firebug 插件深入查看网站的标记和 CSS

images

图 3-5。 Safari 的网页检查器。这些工具有助于了解级联是如何工作的。

这并不意味着理解级联是如何工作的不重要,但它使调试过程不那么痛苦。你可以在第十章中了解更多信息。

编码

虽然 CSS 很少出现问题,但值得一提的是,字符编码可能会导致 CSS 文件(和其他文件)出现问题,当问题出现时,它们可能是一个非常现实的问题,并且可能很难隔离。通常,文件将以默认的区域设置编码保存在您的计算机上,这是 ISO-8859-1,适用于使用拉丁字符集的西方国家。这个字符集不支持许多字符,如重音字符或不常见的符号。实际上,只有三个地方会出现问题:

  • 使用添加内容的伪选择器,如:after:before
  • 引用路径中包含异常字符的图像或文件
  • 可能包含异常字符的注释

尽管使用 Unicode 字符集会(非常小地)增加文件大小,但坚持使用 UTF-8 可以避免令人痛苦的兼容性问题,有助于提高文档的可读性,并避免本地化和转义字符的问题。如果您可能要转义许多字符,这实际上会节省字符和文件大小。关于为什么应该避免逃逸的更多信息,请参考[www.w3.org/International/questions/qa-escapes](http://www.w3.org/International/questions/qa-escapes)

请仔细考虑这一点,并将其包含在 CSS 格式指南中。如果您可能会使用默认字符集中不包含的字符,我们建议您使用 UTF-8。

本地化

如果您的网站需要在多个不同的国家工作,本地化可能会成为一个问题。对于由内容管理系统(CMS)或其他方式生成的动态内容,您的文本可能具有不可预测的大小。特别是德语和汉语是非常冗长的语言。有两种方法可以解决这个问题。

首先,你可以把每一个容器都做成流体的,并根据其内容改变大小。在某些情况下,这可能是可以实现的,但在许多情况下,这可能变得不切实际。

其次,你可以给你的 CSS 一些特定国家和语言组合的钩子。您可以使用服务器端代码在您的主样式表之后为这些特定的国家和语言包含一个额外的样式表,这样您就可以为它们实现覆盖,如下所示:

<link rel="stylesheet" href="css/en-gb/overrides.css" />

在本例中,我们使用了具有特定命名约定的子文件夹。我们使用了 ISO 639-1 标准的语言代码——“en”——简单明了;然后是连字符;最后是国家代码的 ISO 3166-1 alpha-2 标准([en.wikipedia.org/wiki/ISO_3166-1_alpha-2](http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2))—“GB”。 2

这种方法会导致性能下降,因为有一个额外的请求,但它也确保了我们不会试图在一个文件中满足所有国家和语言的需求,这将是低效的。另一个缺点是我们的规则是不同的,我们必须维护许多文件;当修改我们主要语言的 CSS 时,我们可能会忘记修改其他国家和语言的相应规则。


虽然 ISO 3166-1 的所有内容都是大写的,但是我们把所有内容都用小写的,这样有助于压缩和保持简单。

针对这些语言的另一种方法是向htmlbody标签添加类,如下所示:

<html class="en-gb">

这使得以这种语言为特定目标的页面变得容易,并且将我们所有的 CSS 保存在一个地方,但是不利的一面是会产生非常冗长和低效的 CSS,因为我们可能会向永远不会使用它的用户提供大量的代码。出于这个原因,我们推荐第一种方法,在主样式表中使用注释来提醒开发人员其他文件包含对相同规则的引用。

另一个本地化考虑是一些语言从右向左流动(RTL),而不是从左向右(LTR)。对于这些,您几乎肯定需要使用不同的模板,以便您的标记处于正确的顺序,然后您可以使用上面详细介绍的方法来确保一切看起来和读起来都像它应该的那样。

images 提示:使用 accept-language HTTP 头,您通常可以检测用户设备的语言,并以正确的语言返回他们的内容,这是一种他们会欣赏的好方法。您也可以根据他们的 IP 地址来检测他们的位置,但这通常是不准确的。HTML5 ( [dev.w3.org/geo/api/spec-source.html](http://dev.w3.org/geo/api/spec-source.html))中的地理定位 API 要精确得多,但还没有得到很好的支持。

如前所述,CSS 和标记文件坚持使用 UTF-8 可以省去很多麻烦,但是您可能需要根据自己的需求为特定国家实现其他字符集。

特定于浏览器的 CSS

在大规模和高流量的网站上工作通常意味着必须迎合各种各样的浏览器。不是每个人都会使用最新版本的最现代的浏览器,而且通常的决定是,用户不应该因为可能不是他们的错的障碍而受到惩罚。

根据组织选择支持的浏览器(参见第六章中关于分级浏览器支持的更多内容),几乎总是需要将特定的 CSS 定位于不完全兼容的浏览器,如 IE 7 或更低版本。

有各种方法可以实现这一点。根据此类浏览器和网站设计本身所需的支持级别,主 CSS 文件和特定于浏览器的文件之间的差异可能会有所不同,从对几个属性的少量调整到许多应该拥有自己的单独 CSS 文件的选择器。

意见不一。独立样式表的支持者提出这样的论点,即这将有助于保持主文件的有效性和免受黑客攻击,使得以后删除特定于浏览器的文件也更容易。其他人会说更新两个或更多不同的样式表是不实际的,验证应该只是一个工具,而不是目标。有些人甚至会争辩说,因为当我们处理特定于浏览器的 CSS 时,我们经常求助于黑客——而黑客是“丑陋的”——我们样式表上的这些“污点”不应该被隔离,而是非常明显和容易发现的。拥有更多的 CSS 文件对性能有着的影响,你可以在第八章中读到。采取哪种立场由你的公司决定,因为两者都有自己的好处。

如果您有少量针对(例如)IE 6 和 ie7 的特定修复,通常最有效的方法是在默认属性声明旁边包含针对这些浏览器的 hacks,并带有解释其用法的注释。将属性和它们的 hack 副本放在一起,很明显,当更新一个时,应该更新另一个。使用外部样式表来应用小的更改(即使对少数人而言)会对这些浏览器的用户造成大的性能影响,并且很少是解决这个问题的最佳方式。然而,如果需要使用大量的黑客攻击,并且你所针对的浏览器给你的网站带来的流量很小,或者对你网站的目的来说不重要,那么为了给你的主要用户带来更好的体验而惩罚这些用户可能更好。

您应该密切关注您的站点可用的报告工具,并非常清楚与您最相关的浏览器和设备。记住这些数字,您应该用您通常的 CSS 和单独的样式表中的 hacks 来测量 HTTP 性能,以便很好地了解每种方法的优缺点。有些人主张在页面中的条件注释内嵌入非常小的代码,但是我们不推荐这种方法,因为它鼓励不同的代码(在许多不同的地方有相似的代码),并且取消了对单个文件缓存的控制。

如果 CSS 文件中的 hacks 数量占总代码的 20%以上(在缩小、连接等之后)或超过 50 KB,出于性能和带宽的考虑,将代码移到外部文件中可能是可行的。如果使用单独的样式表,那么将它们连接起来是非常重要的,这样用户可以得到尽可能少的额外文件。

正如我们以前多次说过的,最重要的是进行对话,做出决定,并始终如一地执行决定。但是,还有其他方法可以将 CSS 应用于特定的浏览器和设备,如下面的内容所讨论的。

黑客和过滤器

有几种方法可以使 CSS 属性和选择器仅对一个(或多个)浏览器可见,也可以对某些浏览器隐藏它们。

我们不会详尽地列出这些技术,但我们会提到一些最常用的技术,这样您就可以了解它们的样子和用途。从维基百科([en.wikipedia.org/wiki/CSS_filter](http://en.wikipedia.org/wiki/CSS_filter))开始,网上有几个资源,提供了这些黑客攻击的完整目录,因为它们现在已经众所周知,并有完整的记录。

盒子模型黑客

被公认为有史以来第一个 CSS hack,box model hack 用于为 IE 5 和 5.5 提供不同的措施,因为它们的盒子模型是坏的(因此得名)。我们将在本章中进一步解释 IE 盒子模型。由于浏览器解析错误,仅处理第一个宽度(来自以下示例)。兼容的浏览器将理解这两个宽度值,用第二个值覆盖第一个值:

.box {    width: 600px;    voice-family: "\"}\"";    voice-family: inherit;    width: 560px; } html>body .box {    width: 560px; }

使用 voice-family 属性不会影响屏幕样式表。没有解析错误的浏览器会将第一个voice-family值读取为“}”,而有解析错误的浏览器会将整个规则解释为以包含在voice-family值中的右括号结束,忽略后面的所有内容。第二个voice-family属性是这样的,万一名为“}”的文件确实存在,这个值可以被覆盖。第二个规则适用于同样会遇到解析错误,但是有正确的盒子模型的浏览器。IE 5 和 5.5 忽略它,因为这些浏览器不理解子选择器(用“>”表示)。

下划线黑客

我们可以通过在属性名前添加下划线来为 IE 及以下版本提供不同的属性值,如下所示:

#logo {    background-image: url(logo.png);    _background-image: url(logo.gif); }

因为以非转义下划线开头的属性是无效的 CSS,所以其他浏览器会忽略第二个属性。另一方面,IE 6(及更低版本)处理下划线的方式是忽略它,并像平常一样解析属性,用第二个值替换第一个值。

明星钻营

star hack 的目标是 IE 7 及以下版本。原来 IE 6 对待星号的方式和对待下划线完全一样。因为对下划线的错误处理引起了如此多的关注,微软在 IE 7 中让浏览器正确处理了下划线。然而,它没有修复星号。通过在属性名前面加一个星号,它将被所有其他浏览器忽略,其工作方式几乎与前面的攻击(下划线攻击)相同:

section {    width: 860px;    *width: 960px; }

这种不一致性使得 IE6 和 IE7 可以分别作为目标,如下所示:

section {    width: 860px; /* all browsers */    ***width: 960px; /** IE7 and below */    _**width: 1060px; /** IE6 and below */ }

因为最后一个规则总是优先,对于解释以下划线开头的属性的浏览器(IE 6 和更低版本),前面的属性被覆盖。

明星 Html Hack,IE4-6

因为 IE 4 到 6 错误地在 DOM 中根html元素之前包含了一个不可见的神秘元素,通过使用 star html ("* html") hack,您可以在一个规则中针对这些特定的浏览器:

* html h1 { background-image: url(logo.gif); }

因为在任何其他浏览器中,html元素之前不存在任何元素,所以所有其他浏览器都将忽略整个选择器及其所有属性。

子选择器破解

IE 6 及更老版本不理解子选择器(用“>”表示)。因此,我们可以用它来隐藏这些浏览器的规则。例如,下面代码块中的第二条规则,我们将透明的 PNG 背景应用到一个div(IE 6 不支持),ie6 不会读取它,使它使用前面提到的普通背景色:

div {  background-color: #dd4814; } body > div {  background-image: url(orange.png) no-repeat; }

被评论的反斜杠 Hack

IE for Mac 有自己的特点(尽管它已经不再被普遍使用)。这个浏览器选择(违反规范)让你用反斜杠转义注释中的星号。我们可以利用这个事实,因为这个浏览器不知道以某种方式编写的注释是关闭的,因此不会读取位于转义字符和普通注释之间的 CSS:

/* IE for Mac doesn’t understand this comment is closed \*/ (anything between the comments won’t be read by IE for Mac) /* IE for Mac will continue reading the CSS file after this comment */

Mac IE 浏览器的上一次发布要追溯到 2003 年,微软在 2005 年就停止了对这款浏览器的支持,因此这款浏览器的流量不太可能成为问题。

不可避免的灾祸

虽然我们不喜欢鼓励使用这些所谓的“过滤器”,但我们知道它们在某些情况下可能是有用的,并且由于时间或预算的限制,使用它们而不是花费数小时试图解决问题的根源可能是必要的。分离特定于浏览器的 CSS 的另一种方法是使用条件注释,这通常被认为是一种更干净的解决方案。我们将在本章后面讨论条件注释。

CSS 表达式

CSS 表达式(或动态属性)是在 IE 5 中引入的,直到版本 7(或 8 和 9,当在兼容模式下渲染时)都受支持。通过将 JavaScript 放在表达式中,我们可以根据环境或其他因素返回不同的结果。

连接两个字符串的简单 CSS 表达式(记住这是 JavaScript,因此语法不在本书的讨论范围之内)如下所示:

aside {    width: expression("320"+ "px"); }

这与具有以下内容是一样的:

aside {    width: 320px; }

在下面的示例中,表达式检查正文宽度是否小于 1200 像素;如果是这样,宽度设置为“1200 像素”,如果不是,则设置为“自动”:

#container{    width: expression((document.body.clientWidth > 1200) ? "1200px" : "auto"); }

CSS 表达式是资源密集型的,会严重影响网站的性能。雅虎!在其高性能网站规则系列中,将规则 7 列为“避免 CSS 表达式”,认为“它们不仅在页面呈现和调整大小时被评估,而且在页面滚动时,甚至在用户将鼠标移动到页面上时也被评估。”([developer.yahoo.net/blog/archives/2007/07/high_performanc_6.html](http://developer.yahoo.net/blog/archives/2007/07/high_performanc_6.html))。

有些表达式可能比其他表达式具有更强的性能,因此总是建议进行性能测试(与所有 JavaScript 一样),因为在某些情况下,使用一个 CSS 表达式是处理浏览器不一致性的最快和最有效的解决方案。

CSS 表达式的另一个缺点是它们依赖于 JavaScript 来工作,这远非理想,因为我们实际上是在处理 CSS,并且用户可能禁用了 JavaScript。鉴于我们正在创建一个 JavaScript 依赖项,我们的建议是将它们转移到 JavaScript 中,使 CSS 远离 CSS 表达式,避免随之而来的性能损失。

很少有跨浏览器的情况实际上需要 JavaScript 来解决,像 CSS expressions 这样的事件驱动解决方案就像高速公路上的砖墙一样微妙。除非你有非常充分的理由使用它们,并且这是最后的手段,否则我们建议你不要这样做。

供应商特定的扩展

特定于浏览器的 CSS 不仅仅意味着处理有缺陷的 IE 渲染和解决它的怪癖。它还可能包括使用特定于供应商的属性来创建更高级的效果,这些效果符合最新的 CSS 开发或者还不是任何规范的一部分。

例如,创建 CSS border-radius的语法需要为已经以实验(并且通常不完整)方式实现该属性的浏览器编写,使用其特定于供应商的版本以及官方规范:

.box {    -moz-border-radius:  4px    -webkit-border-radius: 4px;    border-radius: 4px; }

在这个例子中,我们基本上对同一件事重复了三次,以适应尽可能多的浏览器。我们已经包含了规范中最后提到的属性,所以当所有浏览器最终实现它时,它仍然可以工作,并且正确的符合标准的实现将覆盖特定于供应商的版本。 3

这会导致一个无效的 CSS 文件,因为特定于供应商的扩展仍然会被 CSS 验证器解析为错误(尽管有些人已经开始要求改变这种行为)。但是,请记住,浏览器制造商使用这些特定于供应商的扩展是标准做法,在 W3C 规范中有所预见,其中规定“以‘-‘‘_’开头的属性名是为特定于供应商的扩展保留的”,并且它们“保证永远不会被任何当前或未来级别的 CSS 用于属性或关键字”([www.w3.org/TR/CSS21/syndata.html#vendor-keywords](http://www.w3.org/TR/CSS21/syndata.html#vendor-keywords))。供应商之间应该没有冲突,因为两个供应商不太可能选择相同的前缀。然而,这些实验性属性的实现有可能(并非不可能)在它们仍处于“测试”阶段时发生变化——如果您不想大吃一惊,那么了解 W3C CSS 工作组的最新消息是一个好主意。

下面是最常用的特定于供应商的前缀列表:

images

尽管这些属性是无效的、特定于供应商的 CSS,但它们不能被称为黑客或变通方法,它们是对可能成为最终规范一部分的属性的支持。将它们局限于自己单独的样式表可能会在寻求效率的过程中出现失误:当其中一个属性需要更改时,所有其他属性也需要更新——来回切换可能会令人生畏。同样,CSS 文件越多(因此 HTTP 请求越多),性能受到的影响就越大。


3 最新发布的浏览器如 Safari 或即将发布的 IE 9 支持border-radius而不需要厂商前缀,因此包含默认属性是永远不要忘记的重要一步。

4 由于 WebKit 最初是 KHTML 的分叉版本,所以它使用了用于 CSS 属性的-khtml-扩展,而-apple-扩展用于特定于 Apple 的功能。这些最终被合并到了-webkit-扩展中,尽管仍然有一些属性是 Safari 专用的,并且使用了-apple-扩展(例如,-apple-line-clamp)。Konqueror 浏览器仍然使用-khtml-扩展。

其他解决方案,例如将这些特定于供应商的属性放在主 CSS 文件中的一个单独的部分,使它们更容易找到(因此,在需要时更容易删除),可能是一个更好的节省劳动力的解决方案,尽管它会导致重复的选择器,效率更低,更难管理。

媒体查询

我们鼓励使用功能检测,而不是使用黑客或其他方法只为某些浏览器提供特定的 CSS,因为这是一种面向未来的明智方法,不依赖于错误或容易被覆盖和欺骗的属性。媒体查询允许创建依赖于用来查看网站的设备的功能或特征的 CSS。例如,您可以编写只在单色屏幕、小视窗、打印机甚至两种或多种功能组合的设备上显示的 CSS。媒体查询不针对特定的浏览器,而是针对显示网站的设备的功能。

媒体查询有三种不同的应用方式:

  • 直接在样式表中
  • 从样式表中导入
  • 在 HTML 文档的head中使用链接标签

下面是一个直接嵌入到样式表中的媒体查询示例,与 CSS 的其余部分放在一起:

@media screen and (max-width: 320px) {    aside {       float: none;    } }

您可以使用@import 规则从样式表导入不同的文件:

@import url(320.css) screen and (max-width: 320px);

最后,在 HTML 文档的head内的link标记中插入一个媒体查询:

<link rel="stylesheet" media="screen and (max-width: 320px)" href="320.css" />

当创建网站的移动版本时,媒体查询经常被引用,但是除了纯粹的设计,还有其他因素你应该考虑。使用媒体查询不一定会阻止图像被下载。 5 事实上,在使用link标签时,无论媒体属性如何,样式表都会被下载,尽管它们可能不会被应用。虽然媒体查询可以应用于创建网站的快速移动版本,但在处理高流量网站时,性能和可访问性考虑因素(许多移动设备仍然不支持媒体查询)可能(也应该)位于列表的顶部,并可能在很大程度上使它们的使用无效。我们将在第七章中详细讨论媒体查询。


5 WebKit 下载图片设置为display:none;火狐和 Opera 没有。这适用于桌面和移动设备。它更适用于媒体查询,因为如果媒体查询适用,则不下载背景图像,但如果媒体查询覆盖已在通用样式表中应用的另一个背景图像,则不下载背景图像—在这种情况下,两者都下载。基本上,要知道通过 CSS 隐藏东西并不意味着浏览器不会下载它们。

条件注释

条件注释是在 IE 5 中引入的,是特殊格式的 HTML 注释,允许针对特定版本的微软浏览器。它们可以被包装在另一个 HTML 块中,并有效地隐藏起来,不让非目标浏览器看到。

条件注释可用于针对 IE 的一个特定版本:

<!--[if IE 8]>         <link rel="stylesheet" href="ie8.css" /> <![endif]-->

在这种情况下,ie8.css 文件只会被 ie8 请求。

它可用于定位低于或高于特定版本号的版本:

`

`

在第一个例子中,我们的目标是从 5 到 6 的 IE 版本(lt代表“小于”)。在第二个例子中,我们使用了gt操作符(“大于”),目标是高于 7 的版本,但不包括 7(在撰写本文时,将包括 IE 8 和 9)。

它还可以用于比特定版本更低或更高的目标版本,但包括指定的版本,如下所示:

`

`

第一个例子针对的是从 5 到 6 的 IE 版本,使用了lte操作符(“小于或等于”)。第二个目标是 IE 7 以上的所有版本,包括 IE 7 ( gte代表“大于或等于”)。

也可以使用 NOT 运算符("!"如在许多编程语言中使用的),以便对一个或多个浏览器版本隐藏注释。以下条件注释将被 IE 5 忽略,但不会被其他版本的 IE 忽略:

<!--[if !(IE 5)]>         <link rel="stylesheet" href="advanced.css" /> <![endif]-->

虽然条件注释并不常见,但随着表达式的使用,它会变得更加复杂,比如 AND 运算符("&")。这里有一个例子:

<!--[if (gte IE 5)&(lt IE 8)]>         <link rel="stylesheet" href="hacks.css" /> <![endif]-->

这里我们针对的是高于并包含 IE 5 和低于 IE 8 的 IE 版本,所以版本 5 到 7 包括在内。我们也可以在这些表达式中使用 OR 运算符(" | ")。

条件注释有时会被列在其他黑客中,但与它们不同的是,它们不依赖于浏览器漏洞来工作,这使得它们使用起来更安全,更经得起未来的考验。它们提供了一种更干净的方式来将特定于浏览器的 CSS 从基本或更高级的样式表中分离出来,如果对旧浏览器的支持下降,则更容易删除这些样式表。

为了减少 HTTP 请求,包含在条件注释中链接的文件中的 CSS 可以直接嵌入到 HTML 文档的头部,如下所示:

<!--[if lte IE 6]>         <style>            li {               display: inline;                 }         </style> <![endif]-->

这比链接到每个浏览器的外部文件更有优势,因为每个浏览器都需要单独的样式表,尤其是在只需要几个规则的情况下。这种做法可能会妨碍这段代码的可维护性,但是如果在页面性能方面的优势更大,这是一个值得考虑的解决方案,并且一些高流量网站已经在使用它了。每个请求都有非常真实的性能含义。

在文本节点或属性中不能使用条件注释。例如,这是行不通的:

<style>    li {       display: block;    }         <!--[if IE 6]>            li {               display: inline;                 }         <![endif]--> </style>

这也不会:

`


   …

`

针对除 IE 以外的浏览器,即隐藏所有 IE 浏览器的内容,有必要使用“下层显示的注释”,以便条件注释仍可被 IE 读取,但不会在其他浏览器中注释掉内容。这看起来像这样:

`
        

`

这包括围绕条件注释的两个部分的完整注释。使用这种方法,我们可以创建仍然有效的标记。

html 元素中的条件注释

为每个浏览器使用单独的样式表会影响 CSS 的可维护性,也会使调试变得不那么简单(如果某个特定的属性或选择器不在主样式表中,您可能不会立即意识到它们正在影响有问题的元素)。将 CSS 直接放在页面的样式标签中可能会节省 HTTP 请求,但是它会导致“关注点分离”([en.wikipedia.org/wiki/Separation_of_concerns](http://en.wikipedia.org/wiki/Separation_of_concerns))的问题——我们的表示逻辑(CSS)不应该存在于我们的内容(HTML)中。考虑到这一点,前端开发人员保罗·爱尔兰提出了一个非常简单但实用的解决方案,它使用了条件注释和页面的html(或body)元素。

使用这种方法,我们有条件地向html标签添加一个类,这取决于使用哪个浏览器来查看页面:

`

`

应用这些条件注释后,我们可以使用 CSS 中的类来创建针对特定浏览器的选择器,如下所示:

#logo {    background-image: url(logo.png); } .ie6 #logo {    background-image: url(logo.gif); }

两个(或更多)样式表总是比一个更难管理,这个简单的技术可以帮助最小化对各种特定于浏览器的 CSS 文件([paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/](http://paulirish.com/2008/conditional-stylesheets-vs-css-hacks-answer-neither/))的需求。

何时以及如何使用黑客技术

CSS 黑客依靠浏览器漏洞来解决渲染问题。这意味着它基本上是依靠一个 bug 来修复另一个 bug,这是很不理想的。

应该避免黑客攻击。当我们使用黑客来处理渲染问题时,我们并没有处理根本问题本身,而是应用补丁来掩盖损害。

尽管这些补丁起初看起来是一个很好的快速解决方案,但如果浏览器得到更新并且错误得到修复,会发生什么呢?(记住,我们在使用 hack 时,通常依赖于两个 bug。)后果将难以预料。

“安全”黑客

新浏览器版本中修复的错误可能带来的问题与旧浏览器无关,旧浏览器在未来不太可能看到任何类型的更新。IE 5.5 或 6 等浏览器有一长串记录在案的众所周知的错误。这些错误中的一些可以通过小心的 CSS 来避免;其他的 bug 会一直存在,但是其他的 bug 更加喜怒无常,以一种不可预测和不一致的方式出现。

因为这些旧的浏览器不再被积极开发(你应该只期待在非常严重的安全问题需要修复的情况下的任何更新,即使这样,CSS 渲染可能也不会受到影响),对它们使用 CSS hacks 往往被认为是安全的做法。我们的观点是,即使在这些情况下,黑客也必须适度使用。了解使用它们的风险是很重要的。由于它们的工作是由于不一致或不良实现的 CSS 解析和边缘情况,未来的浏览器版本也有可能受到它们的影响,尽管这种可能性不大。

作为一个安全攻击的例子,让我们来看看@import攻击,这种攻击通常被用来在 IE 或 Netscape 等浏览器的版本 4 中隐藏 CSS 文件。因为这些版本不理解@import指令,所以其中引用的任何样式表都不会被请求。这个黑客(或过滤器)是由 Tantek ce lik 公开的。它可以在 HTML 文件的head中与style标签一起使用:

<style>         @import "advanced.css"; </style>

它也可以位于实际 CSS 文档的顶部:

@import "advanced.css";

不过,在这种特殊情况下,要注意使用@import声明会导致额外的 HTTP 请求,并且会影响网站的性能。为了避免这些问题,在一个 CSS 文件中使用不超过两个@import声明是常见的做法。(我们将在第八章的中进一步讨论@import声明如何影响下载速度。)

符合最新 web 标准的现代浏览器不应该为了正确呈现 CSS 布局而需要任何类型的攻击。当然,这不会在市场上所有可用的浏览器上产生原始设计的像素完美副本,但这是我们正在使用的媒体中隐含的东西。如果你的公司要求每个浏览器之间的渲染完全相同,那么就有必要展示一下时间成本与功能损失的对比,甚至进行用户测试,以证明用户不会注意到这种程度的细节。如果这仍然是不可接受的,你就必须尽你所能做到最好。明智地选择你的战斗。 6


尽管许多人声称并非如此,但事实是,由于字体渲染差异、浏览器色度、实现,甚至图像中的颜色配置文件渲染,一个网站不可能在所有浏览器中看起来完全相同。

现实世界

当面临需要修改的情况时,理想的情况是重新访问标记和 CSS,看看是否有一个有效的、不特定于浏览器的干净的解决方案,这样我们就可以在第一时间避免这个问题。

另一个好的选择是考虑这个 bug 是否足够相关,可以被修复。例如,如果布局没有损坏,并且我们正在处理浏览器之间几乎不明显的像素差异,那么真的有必要浪费时间和工时来修复它吗?权衡时间成本,以及在此期间可能发生的潜在功能和开发损失,答案几乎总是否定的

尽管最好的解决方案总是编写标记和 CSS 来避免对黑客的需求,但问题是不可避免的,使用黑客是必要的。有时无法访问 HTML,现有的 CSS 无法编辑,或者以这种方式处理问题所需的时间成本无法接受。

在这些情况下,只要有可能,特定于浏览器的攻击应该被归入特定于浏览器的样式表或 CSS 文件中存储所有攻击的部分,这样在不再需要时就可以更容易地删除它们。另一种方法是,可以在黑客的代码行上使用特定的注释,这样以后就很容易找到它们。最常见的例子是 TODO,它被许多集成开发环境(ide)所理解和实现,可以这样使用:

/*    TODO : Remove this when we drop support for IE6 */ _float:left;

以这种方式标记注释使得在开发过程的后期搜索和定位它们变得容易。 7 您甚至可以使用自定义注释,使用易于定位和解析的分隔符,如下所示:

/* HACK_IE7 */ *float:right; /* HACK_IE6 */ _float:left;

如果使用 CSSDOC 类型的注释,那么可以在文件和节注释中使用@todo标记。(CSSDOC 在第二章中有更详细的解释。)

与团队或组织中 CSS 创作的每个方面一样,重要的是要有一套适当的指导方针来确定哪些黑客行为是可接受的,哪些是不可接受的。解决这个问题的一个安全的方法是允许 CSS 作者自由地只应用那些众所周知的和有文档记录的预定义列表中的技巧,并且只针对那些不再被积极开发的浏览器。理想情况下,开发人员还应该知道在什么情况下渲染是完美的或几乎完美的,以及在什么情况下浏览器的不一致性可以被忽略。例如,指南可以提到,对于破坏页面布局的度量,需要对不兼容的浏览器使用黑客技术;对于更多的装饰测量,没有必要的黑客。

服务器端用户代理检测

除了在浏览器或设备本身中,我们还有其他方法为不同的设备提供特定的内容。有时我们只想呈现必要的内容,而不是区分设备所需的所有文件和逻辑。这可以节省用户必须下载的数据量,但是会妨碍缓存,并且会影响服务器端的性能。


7 你可以在[en.wikipedia.org/wiki/Comment_(computer_programming)#Tags](http://en.wikipedia.org/wiki/Comment_(computer_programming)#Tags)阅读更多关于评论标签的内容。

对于服务器的每个请求,在中发送各种额外的信息:与请求相关的额外信息。一些例子是推荐人(用户到达这个地址的站点)、用户设备使用的语言、可接受的字符集等等。

images 注:在将 referer 添加到 HTTP 规范的最初提议中,菲利普·哈拉姆-贝克将其拼错为 referer 。从那以后,它的拼写和实现不一致,但正确的拼写是 referrer。

其中一个头称为用户代理,它是一个文本字符串,代表关于用户设备和环境的各种信息。以下是一些例子:

  • Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.2.9) Gecko/20100824 Firefox/3.6.9
  • Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.13 (KHTML, like Gecko) Chrome/0.2.149.29 Safari/525.13
  • Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/125.2 (KHTML, like Gecko) Safari/125.8
  • Mozilla/4.0 (compatible; MSIE 5.5; Windows 98; Win 9x 4.90)

使用一些简单的字符串操作和条件逻辑,使用这些数据来推断和分类用于访问网站的浏览器是很容易的。我们不鼓励你使用这种方法;但这里有几件事值得一提。首先,用您喜欢的任何东西替换请求头是非常容易的,因此用户可以很容易地模拟他们实际使用的设备之外的设备。 8

第二个是(如前所述)当前的想法阻止我们检测浏览器并以特定的方式呈现给它。我们应该检测能力,而不是设备。然而,这并不总是合适的行为方式或最佳解决方案,因此了解我们拥有的选项是有好处的。

最后,您可能会尝试使用这些技术向(例如)来自 Google 或 Yahoo!希望能索引你的网站。虽然很容易做到这一点,但它被认为是一种黑帽技术,如果搜索引擎意识到你为他们提供的内容不同于为你的用户提供的内容,你可能会受到惩罚,你的搜索结果排名也会受到负面影响。

种情况,检测用户代理是正确的做法。如果您正在为一个特定的设备提供服务,而不考虑它的能力,那么使用用户代理是完全明智的。例如,“我们注意到您正在使用 iPhone!您可以在此下载我们的应用程序”是一个合适的场景。


这不一定是我们需要担心的事情。特别是在 CSS 领域,这并不意味着真正的安全问题,但是插件、浏览器扩展、缓存服务器甚至恶意软件都可能修改这个头,知道这是可能的是很重要的。

下面是一些代码片段的粗略示例,这些代码片段使用一些常见的服务器端语言与用户代理一起工作:

PHP【9】

`<?php
   function detectBrowser() {
      $ua = \(_SERVER['HTTP_USER_AGENT'];       if(strchr(\)ua,"MSIE")) return 'IE';
      if(strchr(\(ua,"Firefox")) return 'FIREFOX';       if(strchr(\)ua,"Opera")) return 'Opera';
   }

echo detectBrowser();
?>`

ASP.NET

`

Your environment: <asp:literal id="envDetails" runat="server" />`

JSP

<% String ua = request.getHeader("user-agent"); out.print ("USER AGENT IS " +userAgent); %>

红宝石

user_agent = request.user_agent.downcase

如您所见,不同语言访问用户代理的方法非常相似。我们想再次重申,我们不推荐这种技术,但是在您的实现中实用是很重要的,我们在这里提到它,因为您可能会发现这种方法解决了您可能遇到的特定问题,我们的目标是成为实用主义者而不是纯粹主义者。


9 Chris Chuld 在[chrisschuld.com/projects/browser-php-detecting-a-users-browser-from-php/](http://chrisschuld.com/projects/browser-php-detecting-a-users-browser-from-php/)有一个全面的 PHP 用户代理检测库。

浏览器渲染差异的一些例子

我们都知道不同的浏览器呈现相同的标记和 CSS 是不同的。不管是不是因为一个 bug,这是 CSS 作者每天都要处理的事情。有了经验,接触到最常见的差异是不可避免的,理想情况下,这将导致更好地了解如何避免或解决这些问题。

虽然在较小的网站中,使用旧浏览器的访问者比例很小,这意味着只有几十人,但这并不适用于高流量网站,在那里,即使不到 1%的用户也可能意味着数万人。这意味着您应该至少熟悉一些浏览器最常见和最具破坏性的怪癖,以避免任何可访问性问题,这些问题可能会阻碍相当多的用户访问内容和浏览网站页面,或者可能会削弱甚至损害您组织的形象。

在本节中,我们将列出一些最常见的浏览器差异,并为您提供一些关于如何计划这些差异或如何修复这些差异的最常见方法的提示。它们是可以在不同层面影响设计的差异,从简单的像素变化到可以破坏布局的错误;虽然在大多数情况下可能需要解决较大的问题,但是是否需要迎合较小的问题应该在团队或组织级别上定义(最好是在 CSS 样式的指南中)。

怪癖模式

旧网站的开发依赖于旧浏览器对 CSS 的不完善呈现,旧浏览器要么遵循未完成或不完整的规范,要么干脆选择不实现 W3C 规定的文档的某些方面。随着新的浏览器版本更加符合标准,这些围绕着漏洞和不一致性开发的旧网站出现了问题,被破坏了。为了保持网站向后兼容(并且不“断网”),现代浏览器经常可以以怪癖模式显示页面,这模仿了老式浏览器的行为。

浏览器通常会根据页面的文档类型声明来决定触发哪种呈现模式(这称为文档类型切换)。完整的 doctype 将触发标准模式(无需页面实际有效),而不完整、无效或缺失的 doctype 将触发古怪模式。如果 doctype 前面有 XML prolog 10 ,IE 6 也会触发 quirks 模式,而当 doctype 前面有注释时,任何 IE 版本都会触发 quirks 模式。 11

迟早,您会面临浏览器呈现差异的问题,您可以将其归咎于页面以怪癖模式呈现。如果编辑标记以应用正确的 doctype 或删除可能触发 quirks 模式的任何其他代码部分不是一个选项,解决方案将不得不依赖于等式的 CSS 部分。

一些怪癖

不同的浏览器有不同的特点,但是这里有一些最常见的特点,它们可能会以更明显的方式影响你的布局。

标准和怪癖模式渲染的一个主要区别就是盒子模型,以及 IE 对它的解释。因为这个问题会对你的网站的整体表现产生更大的影响,我们已经把下面的部分专门用来讨论这个问题。


这是正确的拼写。 XML Prolog 可以声明 XML 中使用的版本和编码格式。这里举个例子:<?xml version="1.0" encoding="iso-8859-1"?>

11 维基百科提供了不同文档类型如何在[en.wikipedia.org/wiki/Quirks_mode#Comparison_of_document_types](http://en.wikipedia.org/wiki/Quirks_mode#Comparison_of_document_types)触发不同模式的综合列表。

边距:自动

影响布局的一个小但重要的区别是margin: auto属性/值。如果你将margin: auto应用到一个有固定宽度的元素上,除非页面在 IE 上以特殊模式呈现,否则它可以在父元素内居中。然而,通过将属性text-align: center添加到父级,IE 7 版本将会错误地将它的块级子级居中,尽管达到了期望的效果,甚至在标准模式下。

表格中的字体属性继承

一些较老的浏览器破坏了表格中字体属性的继承(font-sizefont-stylefont-variantfont-weight),这意味着,例如,如果您为body元素设置了一个font-size,它将不会被表格中的文本继承,而是显示用户代理的默认设置。怪癖模式将模仿这种行为。

溢出

如果overflow被设置为visible,浏览器在 quirks 模式下将扩展容器的大小以容纳内容,而不是保持容器的尺寸不变并简单地溢出内容。无论是否以怪癖模式渲染,IE 6 都有这个 bug。

类名不区分大小写

这一部分的标题没什么可补充的。在 quirks 模式下,浏览器会解释class=**error“与class=**ERROR”或class=**Error”。

颜色值

在光怪陆离模式下,接受不带井号(#)的颜色值。

几乎标准模式

由某些文档类型触发的几乎是标准模式,基本上是标准模式,只是做了一点调整,使得表格单元格内的图像呈现方式有所不同。火狐、Safari、IE 8、Opera (7.5+)都有这种模式。

这种模式和标准模式的不同之处在于表格单元格中垂直大小的实现,它遵循 CSS2 规范。该规范规定图像是行内元素,与文本基线对齐,因此应该为下行字符保留一些空间(小写字符如 pq 有下行字符)。然而,我们都知道图像没有后代。几乎标准模式(就此而言,还有怪癖模式)将在表格单元格内呈现图像,没有那个间隙,消除了那个不方便的底部空间。这对于按照“将图像切片并放入表格单元格”技术创建的网站尤其有用——如果图像下方有空白,布局将被破坏。

这里的建议是,如果您使用过渡标记,您不应该使用触发标准模式的 doctype,而应该使用几乎标准的模式,以避免装饰图像的任何问题。

IE 盒子模型

当文档在可视媒体上布局时,CSS 将每个元素表示为一个矩形框。这些盒子可以一个接一个放置,也可以嵌套放置。CSS3 定义了三种类型的盒子:

  • 块级盒子;例如,一个段落
  • 线盒:例如;一行文字
  • 内嵌级别的框;例如,一行中的单词

每个框都由实际的内容区域组成,并且可以选择具有边框、填充和边距。W3C 定义,边框、填充和边距应该添加到元素框的初始宽度和高度,因此,如果一个div应用了以下 CSS,根据 W3C 规范,div应该具有 124 像素的宽度和高度(100px + 210px + 22px ),所有边都有 20 像素的边距:

div {    width: 100px;    height: 100px;    padding: 10px;    border: 2px solid #333333;    margin: 20px; }

然而,IE 5 以不同的方式解释事情:对于这个浏览器,要从指定的宽度和高度(在这个例子中是 124px)中减去一个框的填充和边框,正如你在图 3-6 的底部图像中看到的。

images

图 3-6。W3C 的盒子模型(上图)和 IE 的盒子模型(下图)的区别

尽管这种解释更符合逻辑,但它是不正确的,这就是为什么这种行为在 IE 6 发布时得到了纠正。然而,如果触发了怪癖模式(如前所述), IE 6 和更高版本将使用其不正确的盒子模型进行渲染。

如果你不做好计划并进行必要的测试,这个错误会带来灾难性的后果,因为它会影响布局中的框的尺寸,并使它们呈现完全不同的大小。

想象一个简单的场景:一个外部容器有 940px 的宽度和 10px 的填充,两个内部盒子有 470px 的宽度,并排浮动。如果 IE 以 quirks 模式呈现,外部容器中的实际可用空间将不是 940px,而是 920px(因为填充是从 940px 中减去的,而不是添加的,这是应该的)。这意味着这些盒子不会并排放置,而是一个放在另一个上面,因为它们不在同一条线上。这只是一个非常简单的例子,说明了对盒子模型的错误解释是如何破坏布局的,这将导致不必要的时间和资源花费在本来可以避免的调试上。

有三种方法可以处理这个 bug:

  • 始终使用正确的文档类型,以便不会触发怪癖模式。
  • 不要在同一个元素上同时指定尺寸和填充/边框(推荐)。
  • 依赖 IE 盒子模型,对较新的浏览器使用 CSS3 box-sizing

通过只指定父元素的尺寸,然后分别指定其子元素的填充和边框,您可以避免这个问题,而不必依赖于是否应用了正确的文档类型。这是 CSS 作者用来避免 IE 6 布局混乱的最古老的技巧之一。在有子元素来实现这一点的情况下,这是一个方便而简单的解决方案。但是,如果不存在子元素,我们建议您使用其他方法专门针对 IE6,而不是仅仅为一个过时的浏览器添加不必要的标记。

另一种选择是使用 CSS3 属性box-sizing,所有现代浏览器都支持该属性,包括 IE 8 (Firefox 需要-moz-box-sizing特定于供应商的属性,而基于 Webkit 的浏览器需要-webkit-前缀)。box-sizing属性接受两个值之一:content-boxborder-boxcontent-box值将应用于 CSS2.1 指定的框模型元素,其中填充和边框被添加到元素的给定尺寸。border-box值将使填充和边框从元素的指定宽度和高度中减去,模仿 IE 框模型。

使用该属性的一个缺点是它必须逐个元素地添加。还可以通过使用通用选择器(*)将它添加到每个元素中,或者将其添加到最有可能需要它的元素中,如截面元素。

总而言之,盒子模型问题是一个可以很容易避免的问题——尽管如果不加以考虑,它可能会产生悲剧性的后果。理想的解决方案是确保 IE 在符合标准的模式下呈现;这解决了许多问题,也是随着浏览器的现代化和向前发展,我们应该努力的方向。我们建议,作为你的防御性 CSS 策略的一部分(你可以在第四章中读到更多相关内容),你可以通过不在同一个元素上指定尺寸和填充/边框来完全避免这个问题。

无论你的团队决定哪种解决方案是最好的,它都应该包含在内部 CSS 样式指南中。

hasLayout

hasLayout 概念是微软创造的,只适用于 IE;它“决定了元素如何绘制和绑定它们的内容,如何与其他元素进行交互和关联,以及如何对应用程序/用户事件做出反应并进行传输”([www.satzansatz.de/cssd/onhavinglayout.html#def](http://www.satzansatz.de/cssd/onhavinglayout.html#def))。

元素的 hasLayout 属性可以是 true 或 false。当它为真时,元素“有布局”默认情况下,有些元素具有布局,而您可以通过 CSS 为其他元素触发该属性。这不是通过特定的 hasLayout 属性而是通过其他 CSS 属性来完成的。

根据微软的说法,以下元素具有布局:

  • html(标准模式下),正文
  • 图片
  • 表,tr,th,td
  • 整点
  • 输入,按钮,选择,文本区,字段集,图例
  • 选取框
  • 框架集,框架
  • iframe,embed,object,applet
  • 绝对定位元素
  • 浮动元素
  • 内嵌块元素
  • 过滤器(微软专有;例如,旋转和阴影)

以下属性/值对将触发 hasLayout 为元素的 true:

  • 位置:绝对
  • 浮动:左/右
  • 显示:内嵌块
  • 宽度:auto 以外的值
  • 缩放:非正常值
  • 写入模式:tb-rl
  • 溢出:隐藏/滚动/自动(IE 7)
  • 溢出-x/y:隐藏/滚动/自动(IE 7)

images 提示:由于 CSS 作者很少使用 zoom 属性,经常添加zoom:1;是一种简单而安全的方法,可以快速给出元素布局,并可能修复大量 IE 错误。如前所述,分离或注释这些特定的修复是很重要的,这样它们就不会与 CSS 的其他部分混淆。

许多 IE 的渲染错误可以通过给出相关的元素布局来修复。由具有(或不具有)布局的元素触发的一些最令人恼火的呈现问题是:

  • 自清除浮动:当内容太长时,浮动会自动清除,而不是伸出其父容器,因此它的父容器会扩展以适合它。在兼容的浏览器中,您必须手动清除浮动才能发生这种行为。
  • 列表元素:一个快速、令人沮丧的例子:当处理一个或多个li元素有布局的有序列表时,那些有布局的元素会将它们的计数器重置为 1(或者您选择应用于项目符号的任何样式的第一个表示)。
  • 绝对定位元素:应该给绝对定位元素最近的定位祖先布局;否则,它们会出现在意想不到的地方。

关于 hasLayout 有很多内容超出了本书的范围。虽然这不是世界上最令人兴奋的话题,但很容易理解当面临问题时,意识到这一点并知道如何最好地处理它是多么重要。由 hasLayout 属性引发的问题是常见的,并且经常以一种不可忽视的方式影响页面的设计。我们建议您阅读位于[www.satzansatz.de/cssd/onhavinglayout.html](http://www.satzansatz.de/cssd/onhavinglayout.html)的综合“关于布局”以获取更深入的信息。

images 注:微软已经修复了 IE 8 和 9 中由 hasLayout 引起的大部分问题,但该属性仍然存在。

实验 CSS

CSS 不断发展,规范不断变化,浏览器供应商也在不断尝试。和任何其他实验一样,随着时间的推移,事情会随着它们的完善而不断变化。这对 CSS 作者来说实际上意味着,尽管我们希望能够舒适地使用像border-radiusbox-shadow这样的属性,知道它们的行为是不变的和跨浏览器的,但实际上浏览器对它们的解释是不同的。差异可能不会很大,可能会导致最终的同质化,但它们是存在的。

这不应该成为使用实验性 CSS 的障碍;恰恰相反。正是通过 CSS 作者对新属性的广泛实现,浏览器供应商才知道他们应该如何以一种实用且符合开发人员实际需求的方式进行操作,这反过来将反映在未来规范的起草上(因为它们是基于实现和示例而不是基于理想主义)。

当在高流量和高知名度的网站上工作时,人们需要更加小心和留意哪些属性可以安全使用,哪些不可以。关注 CSS 工作组的发展,并与新的浏览器版本保持同步是最基本的。参与工作组也是一个很好的方式,让你的声音被听到,成为运动的一部分。尽管在理想情况下,团队中每个处理 CSS 的成员都有兴趣了解最新的更新,但是指定团队中的一个成员负责跟踪最新的消息并让团队中的其他成员了解最新的消息可能是个好主意。建立一个内部邮件列表,所有对此事感兴趣的人都可以订阅,并提供相关文章的链接,这将极大地有利于前端团队。

总结

这一章的主要目的是介绍 CSS 固有的一些基本概念,但是许多开发者容易忽略这些概念。毫无疑问,CSS 很容易掌握,但制作良好编码的样式表,当网站在 IE 中呈现时不会中断(或者只会出现最小的问题),同时保持它们的效率和可维护性,这需要多年的经验和对 CSS 背后的理论的深入了解。

虽然没有必要记住标准模式和怪癖模式下浏览器之间的呈现差异列表(并且可以在 Web 上搜索 hasLayout 问题的最佳解决方案),但是理解为什么会出现这些问题以及在描述它们和定位它们的修复时使用的术语是很重要的。通过了解,我们可以预防,这将在工时和金钱方面为我们的公司节省宝贵的资源。意识到这一点,至少你会知道在谷歌上搜索什么。

在下一章中,我们将看看一些最流行的框架,它们如何以及为什么有用(或无用)以及我们能从它们中学到什么。我们还将看看如何使你的 CSS 更加健壮的一些技巧。

四、框架和集成

当在高流量的网站上工作时,我们希望达到最高水平的效率和可维护性。就 CSS 而言,这意味着我们的样式表应该灵活、健壮并且尽可能小。

在实现这些目标的过程中,我们可以使用一些工具,遵循一些指导方针。任何经验丰富的开发人员都知道,在任何需要构建的时候从头开始通常都是浪费时间和资源。当在封闭的团队和组织中工作时更是如此,这些团队和组织倾向于从事已经(或者应该)定义了基础的项目。

在前面的章节中,我们提到了为了使你的代码和过程一致化,制定指导方针是很重要的。在下一章,我们将会看到一些在你的网站上实现品牌化的技巧,以及一些设计概念在应用于 CSS 时意味着什么。我们还将看看如何建立一个设计库,你可以翻译成你的 CSS 和模块化使用。这一切都与简化开发有关,通过向开发人员提供有意义且他们可以遵循的一般规则,使他们能够做出自己的选择。有了一套明确的指导方针,你也将最小化错误和个人偏好(在你的团队和组织中,个人偏好没有一致性重要)的可能性。

另一个实现高水平的灵活性以及高效率和可维护代码的步骤是拥有一个为您的需求而设计的框架。如果这样一个框架构建得当,它应该是足够模块化和灵活的,这样你就可以依赖它来启动任何一个遵循与公司内其他项目相同的设计准则的项目。在本书的最后一章,我们将从头开始创建一个框架,但是在这一章,我们将看看一些现有的和最流行的 CSS 框架,它们是如何工作的,以及使用它们的利弊。

你所从事的项目的规模可能意味着你的代码不会永远被隔离或者只被负责的人接触。您最终会向 it 部门介绍一名新员工,他不习惯您的流程,可能会不小心破坏一些东西,或者您可能需要集成第三方代码,完全在您的团队或公司之外。

这些都是在高流量、高性能的网站上工作时会遇到的可能性和麻烦,但它们不也让你的工作变得有趣吗?它们很有挑战性,但是你可以做一些事情让你的生活变得更容易。

在本章中,您将执行以下操作:

  • 看看一些最流行的 CSS 框架,包括重置样式表
  • 理解面向对象的 CSS 背后的原理以及它是如何工作的
  • 查看如何处理覆盖 CSS 文件
  • 获取一些关于如何处理第三方代码的提示
  • 理解防御性 CSS 的原理
  • 了解如何避免创建脆弱的 CSS
  • 了解如何在 CSS 中使用元数据

框架

任何有一点经验的 CSS 作者都知道,创建灵活、健壮、跨浏览器的 CSS 布局不是一件容易的事情。这也不是一项独特的任务——尽管我们很想否认这一点,但只有一定数量的网格布局在 Web 上是有效的。我们一遍又一遍地使用相同的元素和相同数量的列——诚然,有一些变化。

这种重复不一定是件坏事;虽然这是老生常谈,但没有必要重新发明轮子(或者,对于体育爱好者来说,改变一支获胜的球队),这就是为什么 CSS 框架在 web 设计师和代理机构中如此受欢迎。它们提供了一个坚实的结构,我们可以在此基础上构建,消除了编码基于 CSS 的布局所带来的一些重复和琐碎的任务。

然而,通过这样做,框架必须能够适应任何设计者选择的任何布局变化。在必要的灵活性和保持代码简洁之间有一个权衡。

框架容易遭受分裂和阶级之苦。虽然 divitis 是一个修饰过度使用div元素的布局的术语,但是classis is(你猜对了)适用于滥用类的样式表。

他们不仅遭受这些弊病,而且框架也倾向于带来各种不必要的冗长代码——就像任何类型的框架一样。因为它们迎合了尽可能多的变化,我们可能会看到类似下面的片段:

清单 4-1。【http://www.blueprintcss.org/蓝图 CSS 框架代码摘录】??

input.span-1, textarea.span-1 { width: 18px; } input.span-2, textarea.span-2 { width: 58px; } input.span-3, textarea.span-3 { width: 98px; } input.span-4, textarea.span-4 { width: 138px; } input.span-5, textarea.span-5 { width: 178px; } input.span-6, textarea.span-6 { width: 218px; } input.span-7, textarea.span-7 { width: 258px; } input.span-8, textarea.span-8 { width: 298px; } input.span-9, textarea.span-9 { width: 338px; }

在前面的例子中,您可以看到 CSS 作者可以用来确定表单输入宽度的类列表。这只是一小段,因为框架列出了 48 个不同类别中的 24 种可能的宽度。

当使用一个现成的框架时,你会留下一些你不需要的代码。您可以使用一些工具来扫描与 HTML 页面相关的 CSS 文件,这些文件会列出页面未使用的任何规则。

其中一个工具的例子是 Firefox 附加 CSS 用法(https://addons.mozilla.org/en-US/firefox/addon/css-usage/),它也是 Firebug 的扩展(见图 4-1 )。这个附加组件将向 Firebug 添加一个选项卡,您可以逐页单击它,它将返回页面中链接或嵌入的所有 CSS 规则的列表,用红色突出显示没有使用的规则。它还显示了正在使用的规则在页面中出现的次数。

images 注意: CSS 的使用将不会通过 JavaScript 影响动态创建的元素。虽然它不是 100%有效,但它是一个非常有用的工具。

images

图 4-1。 Firefox 的插件,CSS 用法,这是一个 Firebug 扩展,在页面上显示未使用的选择器。

这些工具不仅对现有的框架有用,而且在确定您自己的样式表是否需要彻底清理时,它们也是一个有用的工具。

许多 CSS 作者反对使用这样的框架。反对它们的主要论点是,因为它们需要灵活性,框架倾向于使用无意义的类命名,并使它们自己成为非常类似于表格的布局方法。发生这种情况是因为当使用框架编码 CSS 布局时,您必须像处理表格一样可视化布局,包括所有的嵌套和单元格跨度。如果你看一下图 4-2 中的例子,它使用了与清单 4-1 中代码部分相同的网格,你可以看到这种类似表格的方法的一个例子:

images

图 4-2。可视化必要的容器元素,使用框架将布局转化为 CSS 网站

images 注意:在接下来的“替代用途”一节中,我们将介绍如何使用 Blueprint CSS 框架创建这个布局的快速教程。

使用公共框架的一个很大的好处是,新员工更有可能熟悉和适应它们,因此在他们的第一个项目和任务中更快地产生更少的问题和错误。他们也已经被记录在案,可能有一个社区邮件列表,IRC 频道,等等。

有各种各样的 CSS 框架。三个比较有名的是

  • 蓝图
  • 960 网格系统
  • YUI 网格

我们不会推荐一个框架而不推荐另一个——事实上,我们认为对于高流量网站来说,创建一个定制的框架是更好的选择,即使它借鉴了现有的框架。然而,我们将简要地看一看每一个最著名的。

蓝图 CSS

蓝图 CSS 框架由 Olav bjrky 创建,并于 2007 年 8 月发布(见图 4-3 )。它通常被认为是最全面的 CSS 框架,因为它不仅允许创建基于网格的布局,还提供了坚实的排版基础,考虑到垂直节奏等方面。它还提供了重置和打印样式表,以及表单的基本样式。

images

图 4-3。蓝图 CSS 网站首页([www.blueprintcss.org/](http://www.blueprintcss.org/) )

默认情况下,框架使用 24 列布局(每一列跨越 30 个像素,右边距为 10 个像素),但是您可以使用文件中包含的压缩器来创建不同的布局(您可以在官方文档[jdclayton.com/blueprints_compress_a_walkthrough.html](http://jdclayton.com/blueprints_compress_a_walkthrough.html)中找到相关教程)。

使用 Blueprint 就像在页面上的块周围包含一个带有“容器”类的包装容器一样简单。例如,根据每个内部容器的宽度,您需要对跨越整个页面宽度的div使用“span-24”类,或者对跨越 8 列的块使用“span-8”类。如果容器是特定容器或列中的最后一个,它还应该包括一个“last”类(这将删除该容器的右边距,因为它是不必要的)。包含页眉、侧栏、主要内容区域和页脚的简单布局的 HTML 如下所示:

<div class="container">    <div class="span-24 last">       Header    </div>    <div class="span-24 last"> `      


         Sidebar
      

      

         Main content
      

   
   

      Footer
   

`

Blueprint 的优势之一是其背后的社区,它不断地创建和发布新的插件、主题和其他可以与基本框架结合使用的工具。

images

图 4-4。蓝色恩惠网站使用蓝图([www.blueflavor.com/](http://www.blueflavor.com/) )

Blueprint 的核心文件(screen.css,ie.css,print.css)加起来总共 20 KB。

960 网格系统

960 网格系统 CSS 框架(见图 4-5 )由纳森·史密斯开发,并于 2008 年 3 月发布。该框架的开发重点是网格;尽管它提供了基本的印刷样式,但它的主要目的是提供一个跨浏览器的基础(它有完整的 A 级浏览器支持。 1 你可以在第六章中读到更多关于分级浏览器支持的内容,它允许在最常见的基于网格的布局上有许多变化。

images

图 4-5。960 网格系统框架首页([960.gs/](http://960.gs/) )

按照我们用来演示 Blueprint 的同一个简单布局示例,我们将使用 960 Grid System 创建它的一个版本。默认情况下,框架在 12 列或 16 列的网格上工作。我们需要添加一个容器,用“container_16”(或“container_12”)类包围我们的内部块;内部容器应该有“grid_16”、“grid_14”等类,这取决于它们跨越了多少列。下面是我们简单布局的最终 HTML 标记:

<body>    <div class="container_16">       <div class="grid_16">          Header       </div>       <div class="grid_4">          Sidebar       </div>       <div class"grid_12">          Main content       </div> `       


         Footer
      

   

`

1 基于雅虎!在[developer.yahoo.com/yui/articles/gbs/](http://developer.yahoo.com/yui/articles/gbs/)的定义。

和 Blueprint 一样,有几个在线工具可以让你根据自己的需要配置框架(见图 4-6 )。由 Stephen Bau 创建的 Fluid 960 网格系统基于最初的 960 网格系统,但允许流动和固定的布局,并包括导航、表格、表单、文章和排版等元素的基本样式。

images

图 4-6。黑色庄园网站首页,使用 960 网格系统框架([blackestate.co.nz/](http://blackestate.co.nz/) )

简化后,主 960gs 文件(960.css 和 reset.css)占用 12 KB。

YUI 3 格

YUI 3 网格框架是雅虎的一部分!用户界面(YUI)库(见图 4-7 )。该库包括 JavaScript 资源和 CSS (YUI 3 CSS 还包含重置和排版样式表)。它的工作方式与本章介绍的其他框架类似,不同之处在于:主容器没有预定义的宽度;只有预定义的“单位”,其中一个容器可以在另一个容器中扩展。

images

图 4-7。YUI 3 库主页,包含 YUI 3 CSS(框架[developer.yahoo.com/yui/3/](http://developer.yahoo.com/yui/3/) )

为了说明 YUI 3 网格是如何工作的,我们将使用与前面相同的例子。然而,在这种情况下,我们需要向页面的body元素添加所需的宽度,如下所示:

body {     margin: auto;     width: 960px; }

“margin: auto”属性将使我们的内容在页面上居中。接下来,与其他框架一样,我们需要包含一个带有类“yui3-g”的包装容器。其中的容器将根据它们应该填充的宽度百分比(或“单位”)来取类名。所以,举个例子,如果侧边栏占了总宽度的三分之一,就应该有一个“yui3-u-1-3”的类,如果主内容区占了总宽度的三分之二,就应该有一个“yui3-u-2-3”的类。YUI 附带了一组预定义的单元类(列在框架的网站上)。

我们的最后一个示例将包含以下 HTML:

`
   


      

         Header
      

      

         Sidebar
      

      

         Main content
      
      
      

         Footer
      

   

`

缩小的 YUI 3 网格文件占用 4 KB。但是,这仅包括网格,不包括文本默认值或重置。附带的 YUI 重设(本章中提到)、YUI 基础和 YUI 字体也各为 4 KB(总共 12 KB)。

替代用途

CSS 框架的一个常见用途是在原型阶段,即使是那些不喜欢在生产中使用它们的人。因为它们提供了一个可靠的、跨浏览器的 CSS 解决方案,所以使用现有的框架很容易快速创建模型、线框和网站的简化版本。

根据定义,原型(在第一章中有更详细的介绍)是由我们不打算投入生产的代码组成的(至少在其当前状态下),编写原型只是为了创建一个概念证明。记住这一点,与交付时间和资源使用相比,语义、可访问性、过多的标记、文件大小和其他通常至关重要的考虑事项并不重要。事实上,有时更好的办法是削弱你的原型,以避免它成为产品代码,因为它“足够好了”

让我们以图 4-2 中的基本线框为例,使用 Blueprint CSS 框架将其翻译成 HTML。第一步是在 HTML 文档的头部链接所需的 CSS 文件:

<link rel="stylesheet" href="blueprint/screen.css" media="screen, projection" />

接下来,我们需要添加一个带有类“container”的包装器div,以便蓝图文件将其中的容器作为目标:

`
        


        …
        

`

我们的布局由 8 列组成;Blueprint 框架默认使用 24 列,所以我们将在线框图中为每一列计算 3 列。考虑到这一点,顶部块跨越 24 列,左侧列跨越 6 列,右侧(包括 5 个容器)跨越 18 列,依此类推。

如果一个容器是一行中的最后一个,它也应该有“last”类,所以右边距被删除。

我们的线框的基本结构应该是这样的:

`
   


        …
      

      

        …
      

      

         

            

              …
            

            

               …
            

            

               …
            

         

         

            …
         

         

            …
         

      

   

`

接下来的步骤是向这些容器中添加一些简单的文本片段,向容器中添加一个新的类,并在我们的 CSS 中引用该类,以便我们可以使用它来进一步设计主容器的样式(记住,这是一个简单的线框,因此对于我们的示例,我们已经将这一行简单的 CSS 直接嵌入到 HTML 文档中::

<style>         .b { background: #d4d4d4; margin-bottom: 10px; } </style>

在图 4-8 中,你可以看到完成的线框。

images

图 4-8。用蓝图 CSS 框架创建的简单线框

从这个简单的例子中可以看出,现有的框架是创建快速线框和实体模型的有用工具(从阅读入门指南到创建最终产品只花了我们不到 10 分钟的时间)。

重置样式表

尽管有人反对 CSS 框架,但大多数 CSS 作者总是使用其最简单的形式之一:重置样式表。每个浏览器都有一套默认样式,在细节上与其他浏览器不同,如元素的边距和填充、垂直对齐、宽度和高度或字体大小(见图 4-9 )。大多数最新的浏览器在用户代理样式表上相当一致;即使是最新版本的 Internet Explorer 和 9)也在迅速赶上并朝着类似的方向发展。 2 为了克服仍然存在的小的不一致,特别是在旧浏览器中,从一个共同的空白开始,作者求助于所谓的“重置样式表”

images

图 4-9。Safari 5(上图)和 Firefox 3.6(下图)中输入元素的渲染对比。Safari 会在元素周围添加额外的边距。


2 您可以在[www.iecss.com/](http://www.iecss.com/)看到不同版本的 Internet Explorer 中用户代理样式表的比较和演变。

对于重置样式表,有人支持,也有人反对。我们认为,most 列出的缺点可以通过规划来克服,对于一个有经验和知识的 CSS 作者来说应该不成问题。

使用现成的重置样式表的一个缺点是,我们没有利用浏览器的默认设置,稍后我们会在 CSS 中及时添加回来。人们倾向于按原样使用重置样式表,而不根据自己的需要进行调整。这通常会导致 CSS 文件中的大部分规则被覆盖,被那些更为设计考虑的规则覆盖。在这些情况下缺乏计划会给我们留下不必要的规则,这只会使我们的文件更长,更难更新和调试。如果你希望你的网站上的简单列表有默认的项目符号,没有理由在你的重置(或基础)样式表中添加一个“li { list-style: none; }”规则;相反,你应该只设计不需要项目符号的列表,比如导航。如果你网站的标题主要是粗体,那么如果你要覆盖它,也没有理由添加一个规则来声明它们都应该有一个normalfont-weight值。

也许考虑一个更像基本样式表的重置样式表(这是许多 CSS 作者提出的),并在创建它们时包含一些浏览器自己的默认设置,这可能是理解拥有一个共同起点的重要性的更简单的方法,尤其是在团队中工作时。

W3C 已经为 HTML 4 发布了一个“鼓励开发者使用”的 CSS2 默认样式表现在已经过时,不完整,但可以作为参考和起点([www.w3.org/TR/CSS2/sample.html](http://www.w3.org/TR/CSS2/sample.html))。

images 注:有一个非官方的(虽然基于规范的渲染部分,可以在[www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#the-css-user-agent-style-sheet-and-presentational-hints](http://www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#the-css-user-agent-style-sheet-and-presentational-hints)找到)HTML5 版本的这些默认值,由 Jonathan Neal ( [www.iecss.com/whatwg.css](http://www.iecss.com/whatwg.css))创建,W3C 工作组成员推荐。

让我们看几个比较常见的重置方法和样式表。

通用选择器复位

通用选择器复位无疑是同类产品中最小的。尽管这可能感觉像割下你的鼻子来气你的脸,但它的简单性是无可辩驳的,而且与其他重置相比,这种好处还伴随着文件大小的节省。

没有比这更简单的复位了:

{    margin: 0;    padding: 0; }

但这真的能被认为是彻底的重置吗?毕竟,它只涵盖了两个属性,而可能还有其他属性需要重置,以定义一致的跨浏览器基础。除此之外,它还会从您可能不想重置的元素(如表单元素或表格)中删除边距和填充,您以后必须覆盖这些元素。还要考虑到节省的文件大小可能会被你不得不在文件的后面为你的网站上使用的许多其他元素定义边距和填充所抵消。

众所周知,通用选择器在单独使用时不会对网站的性能产生负面影响(当它与其他选择器一起使用时会产生恶劣的影响),但它不是克服浏览器不一致性的完善或完整的解决方案。

images 注意:尽管使用通用选择器的低效率和性能影响似乎是显而易见的,但用当前的工具进行纯粹的跨浏览器研究是不可能的。我们建议不要理所当然地使用选择器,但是它在这里的使用是清晰而实用的。

埃里克·迈耶的重置

Eric Meyer 的重置样式表是最流行的,也是大多数 CSS 作者使用的样式表。这个样式表最初是受 Yahoo!它的主要目的是明确说明哪些元素应该被重置,而不是像通用选择器重置那样使用一个无所不包的选择器。

样式表被仔细地注释,以便 CSS 作者不要忘记样式不应该开箱即用,但事实是许多人选择忽略这些注释,简单地复制和粘贴 CSS。这导致了一些人的评论,他们担心重置(因为它在开发人员中非常受欢迎)会妨碍可访问性,因为它最有争议的规则是:“:focus { outline: 0; }”,在最初的版本中,它之前有一个警告作者定义焦点样式的评论(这经常被忘记),但在最新的版本中已被删除。

Meyer 已经发表了这个重置的几个版本,最新的版本发表在他的博客上,时间是 2011 年 1 月 26 日[meyerweb.com/eric/tools/css/reset/](http://meyerweb.com/eric/tools/css/reset/)

/* http://meyerweb.com/eric/tools/css/reset/    v2.0 | 20110126    License: none (public domain) */ html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video {    margin: 0;    padding: 0;    border: 0;    font-size: 100%;    font: inherit;    vertical-align: baseline; } /* HTML5 display-role reset for older browsers */ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {    display: block; } body {    line-height: 1; } ol, ul {    list-style: none; } blockquote, q {    quotes: none; } blockquote:before, blockquote:after, q:before, q:after {    content: '';    content: none; } table {    border-collapse: collapse;    border-spacing: 0; }

这个最新版本的重置,除了更加小心大纲焦点样式,还包括新的 HTML5 元素,并删除了不赞成使用的 HTML 元素。

images 注意:在 Internet Explorer 之前的版本中,您无法定位新的 HTML5 元素。为了克服这一点,有必要将它们包含在另一个元素中并以包含元素为目标,或者使用一种称为 HTML5 Shiv 的 JavaScript 技巧为每个标签创建一个虚拟元素(不呈现它),这迫使 IE 注意到这些标签的存在。这项技术由 Sjoerd Visscher 发现,由 John Resig 推广,由 Remy Sharp 在[remysharp.com/2009/01/07/html5-enabling-script/](http://remysharp.com/2009/01/07/html5-enabling-script/)完善。这两种方法都有缺点(第一种会产生多余的标记,第二种会产生 JavaScript 依赖),所以在做出决定之前要考虑你的受众。

这是一个相当全面的重置样式表,我们建议您从中获取灵感。但是,我们不建议完全按原样使用它(作者也不建议),因为您可能会发现自己重新定义了一些您以前重置的规则——它将始终取决于您正在进行的设计的需要以及您希望您的元素具有的基线样式。

YUI 3 CSS 复位

最初的 YUI 重置样式表是在 2006 年和完整的库一起发布的。它非常精确地选择了哪些元素以及如何设计。例如,它只删除块级元素和一些表单元素的边距和填充;它解决了表单元素中的字体继承问题;它还增加了浏览器特有的调整字体大小的功能([developer.yahoo.com/yui/3/cssreset/](http://developer.yahoo.com/yui/3/cssreset/))。

以下是最新 YUI 重置的评论版本(在撰写本文时):

/* Copyright (c) 2010, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.com/yui/license.html version: 3.3.0 build: 3167 */ html{    color:#000;    background:#FFF; } body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,code,form,fieldset,legend,input,textarea,p,blockquote,th,td {    margin:0;    padding:0; } table {    border-collapse:collapse;    border-spacing:0; } fieldset,img {    border:0; } address,caption,cite,code,dfn,em,strong,th,var {    font-style:normal;    font-weight:normal; } li {    list-style:none; } caption,th {    text-align:left; } h1,h2,h3,h4,h5,h6 {    font-size:100%;    font-weight:normal; } q:before,q:after {    content:''; } abbr,acronym {    border:0;    font-variant:normal; } /* to preserve line-height and selector appearance */ sup {    vertical-align:text-top; } sub {    vertical-align:text-bottom; } input,textarea,select {    font-family:inherit;    font-size:inherit;    font-weight:inherit; } /*to enable resizing for IE*/ input,textarea,select {    *font-size:100%; } /*because legend doesn't inherit in IE */ legend {    color:#000; }

尽管这种重置经过了多年的改进,但对于您的特定问题来说,它可能不是完美的解决方案。如前所述,您正在处理一种特定的设计风格,并且在这个样式表中声明的一些规则可能必须在稍后的代码中被覆盖,这是没有效率的。

其他复位示例

除了前面提到的复位之外,还有各种其他复位,每种都略有不同。然而,他们似乎从同一套重置(主要是 YUI 和埃里克·迈耶的重置)中汲取了灵感,并分享了其中的许多规则和属性。

blue print 框架将其 reset.css 集成到主 screen.css 文件中,但也在源代码文件夹中提供了单独的文件。蓝图 reset.css 看起来是这样的:

`/* --------------------------------------------------------------

reset.css
   * Resets default browser CSS.

-------------------------------------------------------------- */

html {
   margin:0;
   padding:0;
   border:0;
}

body, div, span, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, code,
del, dfn, em, img, q, dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, dialog, figure, footer, header,
hgroup, nav, section {
  margin: 0;
  padding: 0;
  border: 0;
  font-weight: inherit;
  font-style: inherit;
  font-size: 100%;
  font-family: inherit;
  vertical-align: baseline;
}

/* This helps to make newer HTML5 elements behave like DIVs in older browers */
article, aside, dialog, figure, footer, header,
hgroup, nav, section {
    display:block;
}

/* Line-height should always be unitless! */
body {
  line-height: 1.5;
  background: white;
}

/* Tables still need 'cellspacing="0"' in the markup. /
table {
   border-collapse: separate;
   border-spacing: 0;
}
/
float:none prevents the span-x classes from breaking table-cell display */ caption, th, td {
   text-align: left;
   font-weight: normal;
   float:none !important;
}
table, th, td {
   vertical-align: middle;
}

/* Remove possible quote marks (") from ,

. */
blockquote:before, blockquote:after, q:before, q:after { content: ''; }
blockquote, q { quotes: "" ""; }

/* Remove annoying border on linked images. */
a img { border: none; }

/* Remember to define your own focus styles! */
:focus { outline: 0; }`

960 网格系统的重置也从同样流行的样式表中汲取灵感。以下是 960.gs reset.css 文件的格式化版本(在其原始版本中,该文件被缩小):

html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,addr ess,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u, i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td {    margin:0;    padding:0;    border:0;    outline:0;    font-size:100%;    vertical-align:baseline;    background:transparent } body {    line-height:1 } ol,ul { list-style:none } blockquote,q {    quotes:none } blockquote:before,blockquote:after,q:before,q:after {    content:'';    content:none } :focus {    outline:0 } ins {    text-decoration:none } del {    text-decoration:line-through } table {    border-collapse:collapse;    border-spacing:0 }

从一个巧妙的默认的共同点开始

网上有几十种不同的重置样式表,有些比其他的更详尽。CSS 重置背后的主要思想是,它为您提供了一个跨所有浏览器的共同点,您可以在此基础上满怀信心地构建,您的代码和设计的跨浏览器实现之间不会出现不一致。

当建立一个有数百万用户访问的网站时,最好知道你所创建的东西有正确的基础,并且最令人不快的浏览器默认设置不会进入你不太显眼的页面。然而,这并不意味着您应该使用现成的重置样式表。

作为一个有经验的 CSS 作者,你应该花时间考虑你想为你的页面设置哪些默认值;您应该考虑可以从现有的样式表中使用什么,以及可以从它们中学到什么,但是不要忘记它们的存在和创建主要是为了满足创建它们的开发人员的需求。花时间定制你的基本样式,使它们与你的网站相关:你希望你的 CSS 高效并避免冗余,所以重置一切以便以后再添加不是一个好主意。

因为你应该尽可能减少 HTTP 请求,我们的建议是样式表的重置部分不应该作为一个单独的文件,而应该合并到你的主 CSS 文件中。

为什么要创建自己的框架?

模块化是一个灵活的 CSS 文件系统的核心概念,也是任何从事高流量网站工作的团队的目标。

模块化之所以重要,是因为它允许代码在同一网站的不同部分和同一系列的子网站中重用。如果在构建 CSS 时考虑到了这一点,那么使用代码片段来设计不同页面中相似元素的样式将变得可能和容易,而无需破坏现有代码,也无需嵌入或链接到更大的 CSS 文件。

通过创建您自己的框架,您正在创建一个可重用的代码库,从而提高团队的效率。一个框架不需要太大,但是它应该包含基本的排版和布局变化等元素;表单元素;跨多个站点使用的模块,有小的变化,如导航项目;幻灯片、工具提示或标签等组件。以及在一个或多个站点中易于多次使用的每个元素。它还应该包括网站的印刷和移动版本的单独样式表。

框架(或 CSS 库)带来的可重用性对于效率和文件大小来说是无价的,并且允许更一致和健壮的代码。不同的作者有不同的元素编码方式,比如制表符或者水平导航;如果有一个包含示例或模块的存储库,可以应用于现有的样式表,显示这些元素应该如何显示和编码,那么很大一部分不同的编码样式将被消除。

第十一章包含了创建你自己的 CSS 框架的分步指南。

面向对象的 CSS

面向对象的 CSS (OOCSS)是由前端性能顾问妮可·沙利文引入的概念。Nicole 项目的核心是 CSS(尤其是高性能网站)应该以模块化、灵活的方式处理的概念。

面向对象编程

面向对象编程(OOP) 是一种编程方法,它将代码分解成离散的块(对象),并允许我们重用它们或构建继承其属性的新块。

举个例子,我们可能有一个名为 car 的对象。这个对象有某些属性:它有门、引擎、钥匙、窗户、轮子等等。在许多方面,我们可以把这个对象看作类似于一个模板。我们可能会使用这个模板来创建许多汽车。虽然我们是基于我们的基本模板的汽车,他们可以有更具体的属性;我们可以定义门的数量、引擎大小、颜色、品牌、型号等等。

这种方法同样适用于 CSS,并且很可能您已经不假思索地使用了它。例如,我们的页面上可能有一个我们认为是的对象。为了举例,我们说一个块有绿色边框、圆角和灰色背景。

`

I am a discrete block

I am another discrete block
`

但是,我们可能希望一些块具有更具体的属性,如位置、宽度等:

`

I am a discrete block

I am another discrete block
`

以这种方式使用类允许我们拥有一个基本对象,然后创建继承基本对象属性的更具体的对象。我们甚至可以通过使我们的选择器更加具体来覆盖基本对象的属性。

oocs

尽管术语面向对象可能会引起误解——事实上,这也是许多类似讨论的主题——OOCSS 是编码 CSS 的有效方法。

OOCSS 的主要目标之一是鼓励重用独立于我们试图设计的元素位置的代码片段——CSS 在任何地方都应该完全一样:“不管你把一个对象放在页面的什么地方,它的行为都应该是可预测的,这就是为什么面向对象的 CSS 避免依赖于位置的样式。”([www.stubbornella.org/content/2009/02/28/object-oriented-css-grids-on-github/](http://www.stubbornella.org/content/2009/02/28/object-oriented-css-grids-on-github/))。

这与团队在开发他们的样式表时通常采用的沙箱方法相反——例如,命名空间 CSS 选择器,这在第二章中有更详细的描述。

同样的与位置无关的原则反过来也适用:更改代码的一部分并不意味着破坏其余部分。此外,如果子元素不需要在特定的父容器中才能看起来正确,则父容器不应该依赖其子元素才能在页面上正确呈现。Nicole 将其总结为将内容与容器分离 OOCSS 的原则之一。

在下面的例子中,容器和其中标题的样式规则是分开的,可在其他元素中重复使用——父元素不依赖其子元素来保持正确的外观,反之亦然:

`CSS:

.module {
   margin: 10px;
}
.hd {
   padding: 5px;
   color: #333333;
}

HTML:

   

Title

`

另一个原则建议,控制元素(或对象)结构的规则应该与控制其外观(美学)的规则分开。让我们看一个例子:

`CSS:

.module {
   margin: 10px;
}
.moduleSale {
   background: #efefef;
}

HTML:

   …
`

在前面的例子中,我们用另一个控制组件皮肤的类来扩展组件。控制布局的规则和控制皮肤的规则都可以独立地为其他组件重用。

抽象重复模式以实现可重用性和整体模块化是一种很有帮助的方法,因为你必须基于对设计中所有元素的全面分析,在动手之前考虑如何构建 CSS。在 OOCSS 中,鼓励你首先考虑独立的对象,然后才是页面;您应该能够使用现有的模块化 CSS 构建任何类型的页面。

然而,对于 OOCSS 来说,并非一切都是优势。在拥有一个更小的样式表(其中不惜一切代价避免冗余)和拥有一个更大的、类混乱的 HTML 文档之间有一个折衷。

因为 OOCSS 试图避免使用元素选择器——这样类就不会局限于某个特定的元素——它没有充分利用原始标记的语义。为了避免特殊性冲突,级联还没有发挥出它的全部潜力(事实上,你正在与之对抗)。创建基于类的选择器是为了使每个规则都具有相同的特性,以便能够覆盖(或者更好的是,扩展)其他类。

OOCSS 的另一个弱点,也是反对者最喜欢指出的,是它对类的使用是无意义的。

如果对 CSS 采用面向对象的方法,你将拥有一个“模块”类,而不是一个“侧栏”类之所以会发生这种情况,是因为通过将它命名为侧边栏,我们正在将其本地化,并将其限制在我们页面上的某个位置——我们可能希望在我们网站的主要内容区域内对div使用完全相同的样式,因此命名需要有灵活性。问题是,通过删除这些更具语义性和更易理解的类名,我们似乎正在接近表示性的类名,正如我们前面提到的,这是框架也面临的一个问题。

随着新的 HTML5 元素的引入,这个问题可能会得到一定程度的缓解,比如headerfooteraside。通过使用这些新元素,我们已经在标记中指出了我们想要样式化的内容的基本语义轮廓。当然,永远不会有足够的元素来表明我们可能希望从文档中得到的一切。实际上,除非您使用非常常见的类名或基于标准的类名(比如微格式),否则语义对开发人员的好处大于对用户的好处。我们应该尽可能地追求语义,但这不是 CSS 的全部。

OOCSS 在处理需要灵活但健壮的代码的大型网站时最有意义,并且编码实践的一致性是一个目标。虽然我们同意它有有效的强有力的论据,并赞同它推荐的许多技术,但我们也认为在灵活的 CSS 和灵活的标记之间应该有一个平衡。

良好的语义和可重用的 CSS 之间的平衡对于一个高效的高流量网站来说是至关重要的,将所有的重担都转移到标记上并不理想。如果你的团队与设计团队是分开的,你可能会发现使用这种方法很难遵循隐含的过程。也就是说,有许多 OOCSS 的拥护者,您可能会发现它适合您的需求。

如果你有兴趣阅读更多关于 OOCSS 的内容,请前往[oocss.org/](http://oocss.org/)

覆盖 CSS

CSS 的级联性质允许我们用更具体的样式表覆盖现有的样式表,而不必编辑原始的基本文件。出于各种原因,这可能是必要的,例如季节性的重新设计、每个不同的公司部门需要稍微不同的配色方案、存在从主网站借用的子网站,以及不遵循主样式表中包含的相同设计模式的特殊一次性页面,等等。这些情况必然发生在大的、高流量的网站。

通常,这意味着更具体的 CSS 规则将被放在一个单独的文件中,该文件要么与现有的样式表连接,放在特定页面的 head 元素中的样式标签中(导致关注点分离问题),要么链接到主样式表之后的 HTML 文档,在某些地方覆盖它(尽管在最后一种情况下,您将添加一个代价高昂的 HTTP 请求,这会降低您的网站的性能)。

覆盖 CSS 作为主样式表的补充,通常是在设计中创建不同主题和变化的最有效方式。如果你的样式表是以一种模块化和灵活的方式构建的,它们会被其他更具体的规则所补充,而不需要增加代码的冗余。

然而,如果原因是重大的重新设计,应该避免创建覆盖主样式表的新样式表,因为 CSS 的很大一部分将由过于具体的规则组成,这些规则之所以具体,是因为有一个旧的、过时的样式表需要取消优先级。

当覆盖 CSS 文件时,您将处理的主要因素是特殊性。你希望能够利用已经存在的东西,但是在不需要重复的情况下增加它。为此,明智的做法是从头开始构建主样式表,首先专注于创建合理的默认值,这样就不必为相似的元素重复相同的声明。例如,假设大多数无序列表都应该有特定的样式。下面的 CSS 很难被覆盖(我们在最后的规则中覆盖它):

`#homepage #highlights ul li {
   list-style-type: disc;
   margin: 0 0 1em;
}

homepage #popular ul li {

list-style-type:disc;
   margin: 0 0 1em;
}

homepage #special ul li {

list-style: none;
   margin: 0 0 1em;
   background: url(star.gif) no-repeat left center;
   padding-left: 24px;
}`

执行以下操作会更容易:

`li {
   margin: 0 0 1em;
}

special li {

list-style: none;
   background: url(star.gif) no-repeat left center;
   padding-left: 24px;
}`

如果我们在重置中没有更改无序列表项的list-style-type值,现在我们将利用浏览器的默认设置,通常情况下,无序列表项的list-style-type值是disc。因此,我们没有将它添加到我们的 CSS 中,并且由于我们对所有列表使用相同的间距,除了一些例外,更简单的方法是将其添加为所有列表项的默认样式,然后根据需要覆盖它。

这种思考和计划将产生更小的样式表,更容易更新,也更容易被不熟悉代码的开发人员理解。

许多大型网站选择在页面正文中添加一个类或 ID,作为指示应该使用哪个模板的一种方式。这是一种简单而有效的技术,但您应该小心使用,因为它很容易阻止您重用代码(因为您将为每个模板沙箱化部分代码,所以您需要确保您为一个模板构建的内容不会对所有模板都有用)。如果走极端,当一个新的特殊模板或主题需要到位时,它也会使你走上重复和过于具体的规则的道路。

例如,如果您的样式表有多余的或过于具体的规则,当您想要为网站的某些元素添加圣诞边框时,您可能需要覆盖类似这样的内容:

#home .template-12 #main .col-1 .post img.thumbnail {    border: 1px dotted green; } … #europe .template-8 aside#secondary ol li img {    border: 1px dotted #333333; }

如果 idhomeeurope被添加到htmlbody元素中,可能没有其他方法可以从嵌入的样式表中清除覆盖(让我们避免内联 CSS)。重写以下代码片段会容易得多:

`img { /* You should make an effort to make this initial style as generic as possible. Maybe
you would need a class or an ID before the element selector in your case */
   border: 1px dotted black;
}

.post img {
   border-color: green;
}

secondary img {

border-color: #333333;
}

/* Christmas styles */

christmas img,

christmas #secondary img {

border-color: red;
}`

在你的网站中,图像边框的工作方式可能会有重叠和模式,所以你所做的是将最常见的场景作为默认样式,只在需要时增加更多的细节。

如果采用 OOCSS 方法(本章前面提到过),CSS 的模块化本质意味着引入级联的新文件和规则将倾向于补充现有的文件和规则,而不是覆盖它们。这基本上与我们到目前为止在本节中描述的非常相似,不同之处在于 OOCSS 技术较少利用级联,主要关注于类,避免 id 和类相互补充。在这种情况下,您的 CSS 可能看起来更像下面这样:

`img {
   border: 1px dotted black;
}

.img-post{
   border-color: green;
}

.img-secondary {
   border-color: #333333;
}

/* Christmas styles */

christmas img {

border-color: red;
}`

因为我们正在使用类,christmas ID 足够强大,可以覆盖任意数量的类。

在大型网站中,覆盖现有的 CSS 规则是不可避免的。这还不错;事情就是这样的。既然有可以作为基础的风格,为什么要从头开始呢?

我们在本书中所说和推荐的一切(如何避免冗余,争取模块化和更灵活的样式表,不要在没有必要的时候把事情复杂化,等等)应该给你很好的指导,如何避免使你的 CSS 变得不必要的复杂。

与第三方代码和谐相处

网站开发人员经常会发现自己合并了其他人编写的代码,而他们对这些代码没有控制权。这些代码可能是一个小部件、插件或其他形式的网站块,旨在直接放入您的页面。因为这段代码是为最小公分母场景设计的,所以作者希望它不管周围的代码如何都可以工作——这段代码通常比任何特定情况下的代码都要冗长。如果代码包含 JavaScript 和 CSS,那么经常会出现与网站中已有代码的重复。处理这种情况有两种主要方法:

  • Rewrite the CSS yourself, and opt to not include the third-party CSS.

    This solution gives developers the greatest control over the code provided to users. Although it looks attractive, this method is usually not an effective way of working. If the third-party code is updated in some way (it may be written into the page by external JavaScript), but CSS is not updated, there may be visual or functional problems in the website. Usually CSS will be included in other servers besides your own, which means that you must also use the overlay to modify the code on your site. You should also be aware that when a third party releases a newer and better code version, branching from the original code will make it difficult for you to upgrade easily. The practical method of is usually to accept the third-party code and build around it. If there are so many code problems that this is not an effective method, you should really question whether this third party is a good partner of your business, and look for alternatives or the possibility of writing your own equivalent code internally.

  • Build your CSS to minimize the likelihood of conflicts in your code; Thentest thoroughly.

    This is a more realistic method, although it needs to think ahead. If including third-party code is an unpredictable choice, then you are probably unprepared for this situation.

您可以做的第一件事是尝试将第三方代码从您的代码中分离出来。如果第三方代码被直接放入你的标记中,你应该找到它并弄清楚如何定位它的容器。如果它通过 JavaScript 添加元素,你可以在你的 web 开发工具中观察 DOM(见第十章)并记下容器的细节。如果没有容器,可以考虑自己添加。这将使你有能力定位和修复任何出现的问题。

您可以使用体贴的方法,如果有可能包含第三方代码的特定区域,您可以对它们应用基本的 CSS 重置,只针对那些块。避免使用!important(无论如何你都应该这样做),在使用非常简单的元素选择器的地方,尽量不要命名它们,这样你就最不可能影响具有更高特异性的第三方代码。

您也可以使用可疑的方法,在这种方法中,您尝试使用更具体的选择器,以避免第三方的模糊选择器有机会影响您的页面。

我们推荐体贴的方法,尽管这并不适合每个人。如果你的外部代码写得如此糟糕,以至于影响到你的代码,期望(并要求)他们修复这个问题是合理的。如果您的场景要求您无论如何都要使用该代码,并且它很糟糕,那么您可以自己重写它或者进行彻底的测试,并根据需要为您的标记创建更具体的规则。

走极端的话,您可以包含另一个页面作为 iframe 来完全保护他们的代码不受您的影响。除了最戏剧性和不可避免的情况,我们不建议这样做。

相反,如果你写的代码要包含在别人的页面中,要考虑周全。您应该尽可能地对容器进行命名分隔(使用某种前缀,使您的代码明显地与其他代码分开),以便您可以使用特定的选择器来选择它,然后将它用作应用于块内元素的任何选择器的前缀。不要只以元素名为目标。您可能需要使用命名空间和容器将您自己的重置应用到您的块。

防御性 CSS

当您认为您的代码容易被内部或外部开发人员破坏时,您希望确保采取所有可能的措施来防止这种情况发生。在大型网站和拥有不同实现和 web 团队的大型组织中尤其如此。防御性 CSS 是我们在本书中推荐和提供的技巧。在本节中,我们将概述它们。

可以做几件事来进行防御性编码。请记住,防御性的 CSS 不同于创建健壮的 CSS。健壮和高效的 CSS 是我们最终想要实现的,而防御性 CSS 是实现它的方法之一。例如,确保我们的 CSS 不完全依赖于选择器的顺序不一定是防御性的 CSS,但它是健壮样式表的一个特征。

确保你的样式表被正确地注释和记录,特别是在它们比较脆弱的地方——比如当你求助于黑客或笨拙的解决方案来解决一个别人可能不理解并想要解决的问题——是一种成为防御性 CSS 作者的方法。你应该陈述最初的问题,解释为什么你选择了那个特定的解决方案,提供一个记录解决方案的网站的链接(如果它存在的话),并解释如果有人编辑了那段代码会发生什么(在我们的观察中,我们遇到了几个“如果你编辑了这段代码,一切都会变得混乱”类型的评论,但是我们推荐一些更具描述性的东西)。你可以在第二章中阅读更多关于评论的内容。

采取防御方法的另一种方式是声明对于特定的选择器不一定需要的属性,但是这将防止其他选择器覆盖它。当与命名空间结合使用时,效果尤其好。在下面的例子中,我们声明了一个命名空间规则来设计侧边栏小部件中的标题:

#sidebar #sb-widget h2 {    border: 1px solid #efefef;    font-weight: bold;    padding: 5px 10px; }

如果我们的样式表中有以下不太具体的规则,侧边栏小部件的font-color属性也会发生变化,因为在最初的示例中没有说明:

h2 {    color: #dd4814; }

为了防止这种情况发生,您可以将属性的初始值添加到更具体的规则中,以便它完全保持您想要的方式:

#sidebar #sb-widget h2 {    border: 1px solid #efefef;    font-weight: bold;    padding: 5px 10px; **   color: #1448dd;** } … h2 {    color: #dd4814; }

请记住,这将使这条规则更难覆盖,但这是一个缺点,每次您决定命名部分代码时都必须考虑到这一点。

正如本章前面提到的,OOCSS 是创建防御性 CSS 的另一种方式。重申一下,它的主要原则是父元素和子元素不应该相互依赖,无论它们在页面上的位置如何,看起来都是一样的,而且布局应该与皮肤分开。因此,如果有人对部分代码的工作方式进行了更改(例如,小部件中的标题),这并不意味着子元素、父元素或周围的元素一定会被破坏。

防御性 CSS 的极端例子是命名空间(在第二章中有更详细的介绍)。当您命名代码的各个部分时,您要确保它不会影响代码的其他部分,也不会受其他部分的影响。通过增加它的特异性来隔离它,以便只针对您希望样式化的标记的确切部分。其他任何东西都不会受到影响,所以在这个过程中也不会损坏任何东西。但是,这并不意味着您应该将名称空间作为保护代码的灵丹妙药。它的缺点是代价太大:由于重复和极度缺乏可重用性,你会留下臃肿的代码。这不是在高性能网站上工作的团队应该做的事情。

在处理非开发人员通常使用的内容管理系统时,您必须采取防御性 CSS 方法的最常见场景之一是,从 Word 文档中复制和粘贴文本是经常发生的事情。在这些情况下,我们的建议是,你应该为内容编辑提供他们可以挂钩的类和 id,并给他们来自设计库的示例代码块(我们在第五章中更详细地提到了这一点),这样你就尽可能地为他们做了思考,他们也就有了他们可能需要的一切。

在他们的需求没有得到满足的地方,应该有一个反馈循环:一个让他们请求一个新的设计库元素的过程,他们自己所做的任何改变(在style属性、style标签中的内联 CSS,等等)必须被记录,并在元素准备好时被他们替换。请注意,我们永远不应该阻止内容发布者发布某些东西——大公司通常有时间紧迫的公告或变更要做——但我们应该始终为内容编辑提供一种方式,让他们以正确的方式做他们想做的事情,而不是让所有垃圾 CSS 堆积起来,或者(不切实际地)阻止他们做他们的工作。

当内容编辑不熟悉 CSS 时,巧妙的默认设置和确保所有可能的元素至少有一个基本的样式也有助于克服一些不可预知的情况。

你的团队内部需要一定程度的信任,你应该确保尽可能雇佣最好的开发人员。这对任何人来说都不是新闻,但这并不意味着你不应该试图防止错误的发生。这就是防御性 CSS 的意义所在。它是关于在你力所能及的范围内采取措施,以使错误最小化。

脆弱的 CSS

健壮的 CSS (转化为可以被多人安全处理的灵活样式表)相反,脆弱的 CSS 是容易崩溃的 CSS。发生这种情况有多种原因。

脆弱 CSS 的一个常见情况是 CSS 选择器依赖于页面内容。考虑到我们的 CSS 是一个独立的美学层,这听起来不应该是一个问题——毕竟,无论内容是什么,CSS 都是用来设计内容样式的。然而,我们这样说的意思是,当构建动态网站时,内容不断变化,不一定(或很少)受代码创建者的控制,有时我们应该避免将 CSS 与页面上更可能发生变化的内容捆绑在一起,或者依赖于 DOM 元素的顺序,DOM 元素的顺序也可能发生变化。

比如说,假设你有一个书单;每本书都有标题和封面图片:

`


   
CSS Mastery

   
by Andy Budd

   

Designing With Web Standards

   
by Jeffrey Zeldman

   

`

您希望将第一本书的图像定位在左侧,然后将第二本书的图像定位在右侧。您可以编写以下 CSS:

`#books img[src="book-cssmastery.jpg"] {
   float: left;
}

books img[src="book-webstandards.jpg"] {

float: right;
}`

它会得到您想要的结果,但是如果图像的文件名被更改了会发生什么呢?或者你把这些书换成别的书?这是一个很好的例子,说明将 CSS 与页面内容捆绑得太紧会产生脆弱的 CSS。

这种情况下的另一个解决方案是使用一个nth-of-type选择器来定位特定的img元素:

#books img:nth-of-type(1) {    float: left; } #books img:nth-of-type(2) {    float: right; }

现在我们只针对第一个和第二个图像。这是一个比第一个更健壮的解决方案,但是仍然有些脆弱:CSS 现在依赖于内容的顺序和列表中书籍的数量。

更直接的解决方案是给每个img元素添加一个类,如下所示:

`


   
CSS Mastery

   
by Andy Budd

   

   
Designing With Web Standards

   
by Jeffrey Zeldman

   

`

现在我们可以只使用 CSS 中的类,这就简单多了:

`#books img.odd {
   float: left;
}

books img.even {

float: right;
}`

这种解决方案使得在其他地方重用这些类或者甚至向收藏中添加更多的书变得更加容易。在这种特殊情况下,我们也可以以不同的方式使用nth-of-type选择器:

`#books img:nth-of-type(odd) {
   float: left;
}

books img:nth-of-type(even) {

float: right;
}`

现在,我们不只是设计第一个和第二个实例的样式,而是定义一个可以随着列表的增长而伸缩的样式,它不依赖于任何数量的项目或任何文件名。这种技术有一个缺点:旧的浏览器不理解这个 CSS3 选择器。

images 注意:这是一个你的团队应该清楚的问题:你应该定义旧浏览器对更高级的 CSS 选择器的不支持是否意味着它们被搁置,呈现差异被接受,或者是否应该有某种 JavaScript 回退来复制这种效果。

另外值得一提的是,CSS3 选择器的解析要比简单的选择器复杂得多,因此会对性能造成更大的影响。尽管 CSS 性能很少会成为你的网站的瓶颈,除非必要,否则最好避免它们。

通过不把我们自己和内容捆绑在一起,抽象出这一点 CSS,我们可以更深入到我们在样式表中不断寻找的东西,并创建一个可以在整个页面中重复使用的设计模式。在这种情况下,这将是一个列表,其中的项目显示交替的样式。如果不使用 ID 选择器来定义这个列表,我们可以创建一个具有更方便的名称(允许重复)的类,该类可以在一个页面和网站中多次使用(如果我们保留了“books”的 ID,并且我们需要对一个名为“boardGames”的列表进行样式化,该名称将不再具有语义):

HTML:

`


   
CSS Mastery

   
by Andy Budd

   

Designing With Web Standards

   
by Jeffrey Zeldman

   

`

CSS:

.alt img:nth-of-type(odd) {    float: left; } .alt img:nth-of-type(even) {    float: right; }

继续我们最初的例子,但是现在说明另一种非脆弱 CSS 的情况,而不是依赖于文件的全名,我们可以创建 CSS 来寻找文件名的特定部分并相应地对其进行样式化。例如,您可能希望用一个小图标来设计 PDF 和文本文档的所有链接,向用户表明他们应该从该链接中得到什么。让我们使用上面示例标记的修改版本:

`


   
CSS Mastery

   
by Andy Budd

   
Download a sample of the book

Designing With Web Standards

   
by Jeffrey Zeldman

   
Download a sample of the book

`

使用属性选择器,您现在可以将链接作为目标,这些链接的href属性值以特定的字符序列结束($),如下所示:

a[href$=".pdf"] {    padding-left: 20px;    background: url(pdf-icon.gif) no-repeat left center; } a[href$=".doc"], a[href$=".txt"] {    padding-left: 20px;    background: url(txt-icon.gif) no-repeat left center; }

请再次记住,这些更高级的选择器不会被旧的浏览器所理解。

正如您在前面的示例中看到的,脆弱的 CSS 有不同的级别。然而,脆弱的 CSS 并不仅仅指与标记联系不紧密的 CSS。这也意味着 CSS 会在自身内部分裂,例如,稍后添加的选择器会覆盖原来的选择器,因为它们在文件的后面或更具体。忽略特定性和顺序,并不断添加选择器来覆盖前面的选择器,因为它们来得更晚,更特定,这是做 CSS 的简单方法,不考虑(或不了解)级联是如何工作的。每次需要更新和调试代码时,它都会增加一层复杂性,并且如果开发人员在未来对选择器的顺序进行任何更改,或者只是不熟悉他所继承的纸牌房子,代码很容易被破坏。

这个问题的解决方案不应该是用沙箱保护所有东西,这样当编辑发生时就不会破坏。这会给高流量网站造成另一个有害影响:冗余。所以解决方案是多方面的。正如我们在整本书中所建议的,规划你的样式表以便代码可以被有效地重用,为你将需要的所有元素定义坚实的基础样式,确保有处理遗留元素的指导方针,有一个定义良好的可重用组件的设计库,并且只在必要时添加 id 和类(在 DOM 可能改变的地方,使用它们)。

脆弱的 CSS 往往是复杂的 CSS(虽然不总是)。有经验和知识丰富的 CSS 作者的一个常见行为是编写过于复杂的选择器,以便在不需要接触原始标记的情况下定位元素,而添加简单的类或 ID 实际上会提供更简单、优雅和更有效的解决方案。

类和 id 正是为了这个目的而存在的——这样我们就可以很容易地找到元素——它们就是为了被使用而存在的。作为最简单的选择器,它们也是最快的(特别是 IDs),当在语义上使用时,它们给出了我们的标记上下文和意图。当有他们的位置时,你不应该害怕使用他们。不要试图太聪明,首先考虑最简单的解决方案是否不是最好的解决方案,避免给代码增加不必要的复杂程度,这会使代码效率更低,更脆弱。

CSS 中的元数据

元数据可以被描述为关于数据的数据。HTML 中的meta标签就是很好的例子,它提供了关于文档的额外信息,比如作者、描述、页面语言等等。虽然注释只是为了开发人员的利益,但是元数据可以被 JavaScript 或搜索引擎解析和使用。标签与它们提供的数据类型无关,因为数据的名称和内容存储在结构本身中,如下所示:

<meta name="author" content="Anna Sasin" />

因为你可以有多个meta标签,所以理论上可以包含无限量的信息。

虽然 HTML 为页面中的所有内容提供了包装,并且我们的类和 id(希望)告诉我们一些关于它们所针对的元素的角色,但是 HTML 文档类型定义(DTD)只定义了有限数量的标记和属性。在我们有静态内容的地方,这总是足够的信息来传达我们想要的一切。然而,当我们使用 JavaScript 在页面中移动东西或修改文档对象模型(DOM)时,通常没有足够的信息来实现我们需要的东西。

例如,也许我们有一个表单,其中的字段需要采用特定的格式。它可能是一个订单参考代码,我们知道它总是有三个字母字符和三个数字字符。虽然我们可以使用 HTML5 或其他方法来验证这个字段,但是我们决定(为了举例)在这个实例中我们要使用 JavaScript 来验证这个字段:

<input type="text" id="orderReference" />

对于这种结构,我们不知道如何验证该字段。我们需要在 JavaScript 中找到这个字段,并根据它的 ID(或类)决定它应该是什么格式。我们需要的是向 HTML 元素本身添加元数据的能力。有几种方法可以实现这一点。首先,我们可以向该字段添加一个自定义属性:

<input type="text" id="orderReference" datatype="orderReference" />

然而,这立即使我们的 HTML 无效,因为datatype不是input标签的有效属性。我们可以使用类来保存这些信息:

<input type="text" id="orderReference" class="dataTypeOrderReference" />

这仅仅给了我们布尔值(true如果类存在,或者false如果类不存在),如果我们想要更复杂的值,它们将很难表示。??

JavaScript 有一种存储信息的方法,称为 JavaScript Object Notation (JSON ),这使得以简洁和通用的方式呈现大量信息变得非常容易。对于本例,它看起来像这样:

{dataType:'orderCode'}

如果我们将这些信息放在类中,我们的 HTML 仍然会验证,我们有能力表示复杂的数据,除非我们在 CSS 中定义了非常模糊的命名类,否则永远不会有任何冲突:

<input type="text" id="orderCode" class="{dataType:'orderCode'}" />

虽然感觉语义上不正确,但这种技术的好处大于坏处。


您也可以自己为 HTML 创建一个定制的 DTD,或者在 XHTML 中使用一个特定的名称空间,但是这两个选项都不适合胆小的人,也不能保证文档是有效的。

images 提示:如果你使用 HTML5 作为你的 doctype,你有一个数据属性形式的解决方案。通过在属性前添加数据,我们可以表示任意多的额外字段,如下所示:

<input type="text" id="orderCode" data-datatype="orderCode" data-errortext="Please enter a valid order code." />

但是,这种方法认为所有值都是字符串(字符组),因此很难表示其他数据类型,如数字或布尔值。

我们可能遇到的另一个问题是我们想要显示的错误。我们可以将它包含在 HTML 中,并使用 CSS 来隐藏它:

`

`

由于某种原因禁用 CSS 的任何人,或者使用不支持 CSS 的设备(纯文本浏览器,如 Lynx ( [lynx.browser.org](http://lynx.browser.org))、旧浏览器、一些屏幕阅读器、搜索引擎蜘蛛等)的任何人,仍然会看到这些文本。在我们想要显示它之前,它不应该真正存在于我们的 HTML 中。但是,如果我们在 JSON 结构中包含这些信息,JavaScript 就有了显示错误所需的一切:

<input type="text" id="orderCode" class="{dataType:'orderCode',errorText:'Please enter a valid order code.'}" />

这是一个强大的、真正可扩展的方法,可以在不使代码失效的情况下向标签添加额外的信息,尽管它会产生一个 JavaScript 依赖,并且您仍然应该使用服务器端验证。

流行的 jQuery 库有一个插件,使得在[plugins.jquery.com/project/metadata](http://plugins.jquery.com/project/metadata)获取数据变得更加容易。语法很简单。一旦包含了 jQuery 库和元数据插件,以及前面的 HTML 代码片段,该代码将创建一个警告对话框,显示文本“请输入有效的订单代码”:

alert($("orderCode").metadata().errorText);

你可以在第六章中了解更多实现相同目标的方法。您也可以编写自己的解析器来获取这些值。如果用户禁用了 JavaScript,我们可以优雅地降级并在服务器端处理错误,重新加载带有可见错误消息的页面。

总结

在这一章中,我们研究了现有的框架是如何工作的,我们能从它们身上学到什么,以及它们失败的地方。拥有一个合适的框架所提供的灵活性和效率对于任何一个在高流量网站上工作的团队来说都是无价的,不应该被低估。

除了框架之外,你还可以实现其他原则和编码实践,这不仅意味着你的代码不容易出错,也不容易被内部或外部开发人员或代码破坏。

精心编写的 CSS 可以简洁,但也可以健壮且适应性强。因为它不会永远被隔离,所以您应该计划好它会与其他代码、其他开发人员甚至它自己交互的事实。

在下一章,我们将看看品牌指南的重要性以及如何运用它们。

五、品牌实现

每个人都知道,在网上,我们的竞争对手只需点击一下鼠标。普通用户的注意力持续时间变得越来越短。很容易得出这样的结论:即使你网站的典型用户可能参与度稍高,你也应该确保网站有影响力,品牌体验被正确地转化到网络上。

品牌总是会影响网站的处理方式:它是如何设计的,它使用的语气——基本上,它如何通过在线媒体传达品牌信息。确保正确完成这一任务往往是营销和设计团队的能力。

尽管网页设计和开发团队通常不直接参与品牌建设,但他们面临着在网上实现品牌的挑战。他们还经常面临这样的挑战:让营销团队知道什么是网上可以做的,什么是不可以做的,为什么会有一些惯例(以及什么时候应该和不应该打破这些惯例),以及为什么网络应该被视为一种有机地不同于印刷的东西,而不是一种仅仅遵循印刷视觉识别指南应该做的事情的媒介。

由于本书的主题是 CSS,我们将主要讨论品牌实现的美学方面。诸如品牌文化、处理客户反馈、处理表单和错误信息,或者发展和保持正确的语气等领域都不属于 CSS 领域(但我们希望您正在考虑)。

这一章没有详细介绍如何开发一个视觉品牌。相反,它假设已经存在一个品牌,并且品牌指导方针已经到位。它侧重于如何克服一些困难,以有效的方式实现这些准则。它涵盖了

  • 什么是品牌
  • 使用品牌风格指南和设计库
  • 处理网络上的排版
  • 处理色彩的有效方法
  • 保持布局一致
  • 处理主题变化
  • 品牌演变

Ron Kohavi 和 Roger Longbotham 在 2007 年发表的一篇名为“在线实验:经验教训”的文章中指出,在 Amazon.com,页面加载时间每增加 10 毫秒就会导致销售额下降 1%,而谷歌搜索结果显示时间每增加 500 毫秒就会导致收入下降 20%。

什么是品牌?

品牌通常被认为是公司、产品或服务最重要的资产——这是它区别于竞争对手的地方。但是我们不应该把品牌和仅仅用来识别它的标志混为一谈。品牌可以包含如下属性:

  • 名称、徽标、标语
  • 颜色、排版、图像
  • 语音语调
  • 价值观和使命
  • 客户和反馈处理
  • 店内体验
  • 等等。

基本上,它包括与公司(无论是产品、服务还是组织)被感知的方式相关的一切;它传达给外部世界和内部员工的信息。

品牌有多种类型,但主要分为以下三类:

  • 伞式品牌:这些品牌用于组织内的各种产品和服务,在内部和外部都有使用。例如,Amazon 是 Amazon Marketplace、Amazon MP3、Amazon S3、Amazon Mechanical Turk 等的总品牌。
  • 子品牌:这些品牌虽然是单独推广的,但都与一个伞品牌相关联,以便继承和建立伞品牌的声誉。例如,奇巧、Aero 和 Smarties 是雀巢的子品牌。
  • 单个品牌:这些品牌与它们的伞式品牌完全分开销售。例如,Schweppes、Sprite 和 Dr Pepper 等个别品牌都是由可口可乐制造的,但它们在营销时却没有提到它们的保护伞品牌。

熟悉这些术语(属于品牌架构的范畴)是一个好主意,因为当处理企业品牌时,这个组织经常反映子网站和小型网站将如何设计和开发,以及品牌将如何在它们之间实现。

品牌风格指南

有不同类型的品牌风格指南。有些只涉及品牌的视觉方面,如标志的使用、字体、颜色等。,而其他人则更深入地探讨品牌文化,谈论价值观、工作流程或如何在不同情况下处理客户关系等各种不同的主题。

风格指南通常是在考虑到印刷媒体的情况下开发的。这种情况大多发生在指南不是最近开发的时候,尽管令人震惊的是,现在生产的一些指南仍然不能提供如何在线处理品牌的参考。

当风格指南不能满足网络需求时,营销团队和经理会试图执行为平面媒体制定的规则。这导致了各种各样的问题。

最好的例子之一就是试图让网站页面表现得和纸质页面一模一样。为打印而设计的页面是静态的,打印后不会改变,而在线页面可能会受到内容团队或用户应用的各种更改的影响。长文章会影响页面的高度,更小或更大的屏幕尺寸会引入(或消除)滚动的需要,就像用户样式表可以使文本变大或变小一样,也会影响页面布局和流动。更不用说浏览器呈现字体的方式不同,演示的抗锯齿行为也不同。没有办法确定你的用户会看到你想要他们看到的东西。

这并不意味着在设计和编码在线页面时没有一定程度的控制,但这些只是几个例子,说明专门为印刷格式创建的视觉指南将如何使不直接与网络打交道(或至少不是日常工作)的部门更难理解如何以有机和集成的方式使品牌适应网络。

除了在组织中有一个人的工作是验证品牌的一致性(这个角色通常被认为是品牌经理),所有员工都接受关于品牌应该传达什么的教育是有益的。这不应该仅仅局限于面向客户的角色和营销部门,因为组织中其他领域的人也面临着做出影响品牌认知度的日常决策。

例如,如果一个网站不能适应字体大小的变化,对于有视觉障碍的用户来说,整个体验就会被破坏,他们需要使用更大的字体。或者,如果一个新的页面需要添加到网站上,而它的设计不能恰当地代表品牌,访问者可能会觉得他或她不在同一个网站上,并被迫离开或失去对该网站的信任和好感。

开发者可能并不总是乐于处理影响品牌一致性的设计问题。通常,前端开发人员和 web 设计人员是团队中的不同成员(或者是完全独立的团队——有时甚至不在同一个建筑或国家),有时前端开发人员不得不做出他们可能不适应的设计决策。在这种情况下,灵活且适应性强的品牌指南应该到位,确保网站的一致性,并确保品牌不会因为越来越多的人摆弄样式表或添加新元素而被稀释。

一份全面的网络品牌指南文件应包括以下内容:

  • 关于底层网格、其变体以及如何使用它的信息
  • 排版信息:首选字体、后退和缩放信息
  • 调色板(翻译成十六进制或 RGBA 等 Web 格式)以及应该如何使用它们
  • web 友好单位的度量,如像素、ems 或百分比
  • 通用和可重用元素的规范,比如导航、按钮、小部件、表单、通知等等。

在本文档中提及在不同的浏览器中什么是可以接受的,什么是不可以接受的也是有用的。例如,它可能会指出,无论是否采用 PNG 或其他格式,公司的徽标和主要行动号召必须始终保持一致,以便采取必要的措施来确保这一点。

即使是最彻底的品牌风格指南也不能预见一切。开发者在某些阶段做出设计决策是不可避免的。虽然前端开发人员通常不需要有设计背景,但了解设计如何工作的基本原则将使设计决策落入他们手中的时刻变得更加容易和清晰。对于勤奋的读者,我们推荐马克·博尔顿的网站设计实用指南 ( [fivesimplesteps.com/books/practical-guide-designing-for-the-web](http://fivesimplesteps.com/books/practical-guide-designing-for-the-web)),这是五个简单步骤系列的一部分。这本书非常适合那些没有设计背景,想要实际例子和对基础知识有很好理解的人。

指导方针不断发展

因为大型网站处于不断的变化和扩展中,所以引入新元素、设计新部件、为新部分创建新调色板等等并不罕见。随着设计的发展和成长,指南应该跟随它的脚步。品牌风格指南的主要目的首先是确保一致性;通过将越来越多的不同元素添加到设计人员和开发人员可以参考的中心参考中。为了确保他们没有重复别人已经定义的东西,指南需要保持更新。

如果在一个主要网站中实现了创建第三级导航的新标准,当下一个网站应用它时,它应该被用作参考。保持文档更新的方式可能比实际提出更多模式更复杂。尽管内部 wiki 通常是最简单的设置(特别是对于熟悉使用 wiki 的技术人员来说),但设计和营销团队可能不习惯管理它们。

无论为此目的使用何种技术,最重要的是确保定期更新指南(可能是每周、每月或更少的频率,取决于新指南产生的速度)并将其存储在每个相关人员都可以轻松访问的中心位置;每个人使用一个系统的好处是巨大的。同样重要的是,有人(或团队)负责并监督这个存储库,这样就不会有重复,也不会有与现有元素的差异难以察觉的新元素——如果设计的每个新元素都产生一个新标准,那么就不会有标准。

设计库

对于大型网站,将新标准添加到指南中的过程很可能(也应该)导致设计库的创建,其中所有的模式都存储为片段(图像、HTML 和 CSS)。但是,向库中添加新的模式并不容易——正如前面提到的,大多数时候重用现有的元素比向库中添加新的设计更明智。如果有人觉得有必要这样做,他们应该证明这一点,并确保到目前为止所创建的东西都不符合要求。这意味着品牌和设计有视觉控制,一切保持一致。这也意味着 CSS 不会失控,这使得每个人的生活更容易:更少的代码行,更少的冗余,更大的灵活性,更容易维护。然而,这也意味着引入到库中的元素需要足够灵活,以便可以注入到网站中的各种位置并按预期工作。

想象一下,在你的网站中你使用三种不同风格的列表框:简单框、双框和选项卡式框(见图 5-1 )。

images

图 5-1。设计库中有三个不同的列表框:所有设计都是一致的,但它们有不同的用途。

这个设计很明显遵循了一个基本的模板,但是根据我们需要这个盒子有多突出或者它所容纳的内容类型,有三种不同的变化。对于这些框中的每一个,我们都提供了 HTML,因此不同的内容或开发团队可以使用相同的代码(这甚至可以作为模板文件或可以包含的文件提供,以便在一个地方更改标记会在所有地方更改它)。这里的目标是使 HTML 在框之间尽可能地相似(例如,很明显,简单框和选项卡式框中的标题需要不同的标记,但是双框不需要额外的元素作为内部背景色,您可以使用容器中的列表)。

这些框的 HTML 看起来像这样:

< div class="box">    < h2> Heading< /h2>    < ol>       < li>          < p class="content"> Lorem ipsum…< /p>          < p class="info"> 13 January 2011< /p>       < /li>       < li>          < p class="content"> Curabitur…< /p>          < p class="info"> 28 December 2010< /p>       < /li>    < /ol> < /div>

要在盒子的风格之间切换,我们只需向盒子容器添加另一个类。 2

不需要提供这个 HTML 片段的 CSS,因为我们可以确定它已经存在于网站的 CSS 中,但是图像示例应该显示这些框如何处理不同类型的内容,以及它们在页面中不同位置的行为。提供 Photoshop、Illustrator 或其他分层图稿文件也很有用,设计师可以在设计中复制并粘贴这些文件。这可以节省创建新设计的工作量,并确保它们之间的一致性。


对于选项卡式的盒子,我们将在盒子容器中添加一个类(例如“tab”),并为每个选项卡重复这个过程,用一个类(例如“tab”)将所有的 div 包装在一个容器中,然后使用 JavaScript 重写 DOM。这样,对于禁用了 JavaScript 的用户来说,我们的代码仍然具有语义意义。

很明显,拥有一个针对这种情况的设计库将节省大量时间,这些时间将被浪费在一些任务上,例如浏览网站的现有页面,试图找到类似于我们需要的盒子类型的东西,要求设计师提出已经到位但不容易发现的解决方案(导致许多设计相似,但不完全相同), 或者在数百行 CSS 中复制和改编一些与现有的其他六种解决方案只有细微差别的内容,增加了冗余,降低了灵活性。

这些原则与面向对象的 CSS (OOCSS)所提倡的非常一致:通过可重用的类来实现灵活的 CSS,避免冗余并减小文件大小。这是一种模块化的 CSS 方法,其中每个模块可以放在标记中的任何位置,并且它将正确地工作,使自己适应它的位置,而不依赖于某些父或子来获得它的特征。你可以在第四章中阅读更多关于 OOCSS 的内容。

images 提示: 模块化网页设计,作者 Nathan A. Curtis ( [www.amazon.com/Modular-Web-Design-Components-Documentation/dp/0321601351](http://www.amazon.com/Modular-Web-Design-Components-Documentation/dp/0321601351))在创作自己的设计库时是一个很好的资源。

排版

品牌指南应该为 CSS 作者提供在网站上保持一致排版的基础知识。除了显而易见的类型选择,他们应该陈述细节,如font-sizeline-heightfont-weight、边距和填充以及颜色。

在不进入设计领域的情况下,标题、正文、列表、引用等等有一个清晰的层次结构是很重要的,品牌指南的创建应该牢记这一点。

在网上使用字体是有法律含义的。字体是授权的,不是买的,每个字体代工厂都有自己的规则。您拥有的许可类型决定了您可以在哪里使用它们。以 Adobe 为例,说明了 OpenType 字体在文档中嵌入时可以有四种不同的权限设置类别:

  • 不嵌入:这些字体在任何情况下都不能分享给未经授权的用户。这是最严格的许可形式。
  • 预览&打印:这种许可方案的字体可以以只读方式嵌入电子文档中,例如 PDF 文件。
  • 可编辑:以这种方式授权的字体可以嵌入到电子文档中进行查看、打印或编辑;任何更改都可以保存在初始文档中。字体不能从文档中导出,也不能安装在(未经许可的)最终用户的计算机上。
  • 可安装:这些字体可以永久免费安装在用户的电脑上。

由于在网络上使用字体文件需要通过用户的本地缓存将它们(临时)安装在用户的计算机上,因此可安装类型的许可是唯一一种 100%安全使用的许可。这使得许多字体无法以这种方式使用。一些字体代工厂提供这种字体,但是在一个大型组织中,使用的字体很可能是授权给该组织的,因此不适合以这种方式分发。

Adobe 在[www.adobe.com/type/browser/info/embedding.html](http://www.adobe.com/type/browser/info/embedding.html)提供了关于其许可的更多细节,并在[www.adobe.com/type/browser/legal/embeddingeula.html](http://www.adobe.com/type/browser/legal/embeddingeula.html)提供了其字体和许可细节的列表。如果授权您的字体的字体制造商在其网站上没有类似的列表,那么联系他们以了解他们施加的许可限制是很重要的。如果您在没有正确许可的情况下分发字体,您的公司将承担责任。

然而,在网页中嵌入字体的替代方法可以使用预览&打印许可,只要只使用可用字符的子集。这就是 sIFR(可伸缩因曼闪存替换)如何合法地在网页上显示这些图像。只要一种字体的整个字符集不可用,以这种方式许可的字体就可以显示在网页上。

图像替换与灵活性

在可能需要国际化并不断更新和添加内容的大型网站上,避免使用图像作为使用自定义字体的手段是至关重要的——今天,仅使用 CSS 就可以实现很多功能(我们不仅指 CSS3,也指 CSS2.1)。除此之外(特别是对于链接),搜索引擎认为文本比alt属性更有价值。

虽然像徽标、横幅或主标题这样的重要元素可能会受益于您的组织所使用的自定义公司字体,但在按钮、导航或普通标题上使用图像时,您可能需要三思。

然而,这并不意味着使用非典型网页安全字体(例如 Arial 或 Verdana)是不可能的。随着font-face及其带来的 web 服务的出现(下一节将进一步解释),为各种平台的用户提供定制字体是一项越来越简单的任务。

替换图像的老方法是用背景图像和高负值text-indent来实现的,以隐藏屏幕外的原始文本。例如:

h2 {    background: url(heading.png) no-repeat;    text-indent: -1000px;    height: 30px; }

使用这种方法有一些缺点:启用 CSS 但禁用图像的用户既看不到文本,也看不到背景图像替换(在图像是透明 PNG 的情况下,将文本留在图像后面不是一个选项),图像文件降低了页面的速度,文本不可选择或缩放,并且不容易维护。

这种技术的一些变体包括,例如,在文本周围添加一个额外的元素,然后使用display: none(或visibility: hidden)通过 CSS 隐藏它。没有一种技术是完美的;每一个都显示了一个或多个问题的组合,如 CSS on/images off 场景,使文本对屏幕阅读器不可见或需要多余的 HTML 元素。

图像替换领域的另一个玩家是 Adobe 的 Scene7。它通常被大型组织用作自动创建带有自定义文本的标题或图像的方式。

提供自定义字体的其他选项通常被削减为库芬或 sIFR。

Cufón 依靠 JavaScript 工作,即使它允许选择文本,也没有清晰的视觉指示;sIFR 依赖 Flash 工作,这使得它不适合 iPhone 或 iPad 等设备,或者使用 Flash 阻止插件的浏览器。这两种技术也允许 CSS 作者嵌入字体,这些字体的发行许可证不允许它们在线使用。

Cufón 使用 HTML5 canvas标签(或 Internet Explorer 中的 VML)。与 sIFR 相比,它的一个主要优势是其更简单的实现和字体转换过程(你可以直接在项目的网站上完成:[cufon.shoqolate.com/generate/](http://cufon.shoqolate.com/generate/))。将字体上传到生成器并指定所需的字符(这将减少字符集)后,您需要做的就是链接到 Cufón 脚本和包含字体轮廓的生成的 JavaScript 文件:

< script src="cufon-yui.js"> < /script> < script src="LillyRegular.js"> < /script>

然后,您需要为想要使用 Cufón 的文本调用 JavaScript 函数。例如:

< script>         Cufon.replace('h1'); < /script>

您还可以将生成的字体 JavaScript 表示锁定到特定的域,这样第三方就不能轻易下载和使用它。生成器中有一个选项可以做到这一点。

这两种方法都是可访问的,因为它们可以被屏幕阅读器读取(这使得它同时也是 SEO 友好的)。然而,Cufón 将每个单词包装在一个span中,这可能会导致一些屏幕阅读器将每个单词当作一个完整的句子来朗读,或者更糟糕的是,在每个实例中只朗读第一个单词。3

不幸的是,当用户在浏览器中改变字体大小时,Cufón 不会调整生成文本的大小;从版本 3 开始,sIFR 已经能够处理这个问题。

sIFR 最大的缺点是它使用 Flash(在 JavaScript 之上):如前所述,Flash 在一些设备上被阻止或不可用,所以用它们浏览你的网站的人会看到你的字体堆栈中的下一个可用字体。此外,您团队中的 CSS 作者可能不一定手头有创作 Flash 文件的工具来创建新文本(即使有插件可以使这一过程自动化,如 jQuery sIFR 插件)。这两种方法在执行时都会引入非样式文本(FOUT)的闪现。我们将在本章后面描述这个问题。

可缩放矢量图形(SVG)也可以嵌入字体,并且是可缩放的(与常规图像不同)。 4

所有这些方法都可以被认为是字体嵌入,但其合法性因字体而异。如果对您要使用的字体的最终用户许可协议(EULA)不清楚,我们强烈建议您直接联系字体制造商,查看您可以对该字体做什么。从法律上来说,sIFR 是比库芬更安全的选择,因为 Adobe Flash 被允许嵌入字体,许多字体代工厂在其 EULAs 中迎合了这种情况。Cufón 在网站上嵌入字体,这违反了许多字体 EULAs。

互联网浏览器 6 支持库芬和 sIFR。即使你可以自动化这两种技术的工作方式,它们也总是会消耗资源并增加你的网站的依赖性。像font-face(在下一节中提到)这样更干净的方法应该首先被认为是理想的解决方案。除了小部分文本(如标题)之外,不建议使用图像替换。


3 如果您将“单独”选项设置为“无”,这个问题会得到缓解。然而,Cufón 这样做的原因是它可以将文本换行到下一行。解决第一个问题会重新引入第二个问题。

4 我们不推荐使用 SVG 作为文本替换技术,因为早于 9 的 Internet Explorer 版本不支持它。你可以用 SVGWeb ( [code.google.com/p/svgweb/](http://code.google.com/p/svgweb/))来伪造,但那用的是 Flash,所以你还不如用 sIFR。

在创建网络品牌指南时,将这些因素考虑进去是很重要的。如果这些困难还没有得到解决,网站需要依靠图片替换非网页安全字体,也许现在是更新指南的时候了。

字体面

@font-face 规则是在 CSS3 中引入的,它允许 CSS 作者链接到他们可以在样式表中引用的字体。Internet Explorer 是第一个从版本 4 开始支持这一规则的浏览器(但是只支持其专有格式 EOT)。

images 注: 嵌入式 OpenType (EOT) 是微软为在网页上嵌入字体而创建的专有字体格式。众所周知,将字体转换成 EOT 格式的过程是痛苦的,微软的 Web 嵌入字体工具(WEFT)被大多数网页设计师普遍描述为离折磨不远。幸运的是,现在有其他工具可以实现类似的结果,比如 Font Squirrel 的@font-face Kit Generator(本章后面会提到)。

在撰写本文时,Internet Explorer 平台预览显示支持 WOFF 5 格式。

在@ font-face规则中,各种描述符可以提供像名称这样的信息,这些信息将在文件的后面用于引用字体(font-family;但是要注意 IE 不支持超过 31 个字符的名称)、字体文件所在的位置及其格式(src)或者字体的样式和粗细(font-stylefont-weight)。以下是一个@ font-face规则的例子:

@font-face {    font-family: "Lilly";    src: url("fonts/lilly.eot");    src: local("LillyRegular"), url("fonts/lilly.woff") format ("woff"), url("fonts/lilly.ttf") format("truetype"), url(" fonts/lilly.svg#LillyRegular") format("svg"); } @font-face {    font-family: "LillyItalic";    src: url("fonts/lilly-italic.eot");    src: local("LillyItalic"), url("fonts/lilly-italic.woff") format ("woff"), url("fonts/lilly-italic.ttf") format("truetype"), url("fonts/lilly-italic.svg#LillyItalic") format("svg"); }


WOFF 字体格式是由 Mozilla 基金会与字体设计师埃里克·范·布洛克兰和塔尔·乐铭共同开发的,并且正在成为 W3C 的标准推荐(在由微软、Mozilla 和 Opera 提交之后)。开发 WOFF 时就考虑到了网络。它是一种压缩字体格式,基本上是将字体数据重新打包;TrueType 或 OpenType 等其他格式可以转换为 WOFF,字体许可信息等元数据可以附加到文件中。许多字体代工厂支持 WOFF,因为他们认为它更安全,所以很可能越来越多的商业字体将通过 WOFF 的 font-face 获得使用许可。

@ font-face 规则链接到五个不同的文件位置,因为不同的浏览器支持不同的字体文件格式。“本地”引用是为样式表提供使用本地版本字体的能力,如果它安装在用户系统中的话。还要注意,我们需要声明两个不同的@font-face 规则,以便拥有同一字体的常规和斜体版本。我们本可以使用font-style描述符来声明斜体变体,保持相同的font-family名称,但是 Internet Explorer 和 Opera(10.5 版本之前)将无法正确理解它。

Internet Explorer 在理解一些@font-face 规则语法方面有一些问题。它不理解format()提示或多个位置,并且它试图下载非 EOT 文件,即使它不能读取它们。这就是为什么我们在一个单独的声明中链接到 EOT 格式(后跟一个local() src描述符,它不解析这个描述符)。

添加本地字体名称时,请务必注明 Postscript 版本(如果与全名不同),以便 OS X 上的 Safari 能够理解。

另一个需要考虑的重要因素是,在 Firefox (Gecko)中,字体文件必须与使用它们的页面来自同一个域。这可以通过允许跨站点 HTTP 请求的 HTTP 访问控制来规避。 6

Firefox 支持 TrueType 和 OpenType 格式(从 3.5 开始),而 3.6 版本增加了对 WOFF 的支持;WebKit(525 版起)和 Opera(10.0 版起)支持 TrueType 和 OpenType,以及 SVG 字体;Opera 11 及以上支持 WOFF;Chrome 从版本 5 开始也支持 WOFF;Internet Explorer 从版本 4 开始就支持@font-face 规则,尽管它只接受 EOT 字体。

images 提示: Font Squirrel 提供了一个自动化的“@font-face Kit Generator”,可以更容易地将字体文件转换成不同的格式([www.fontsquirrel.com/fontface/generator](http://www.fontsquirrel.com/fontface/generator))。

字体权限和在系统中安装字体管理器存在一个已知问题,当@font-face 规则尝试使用本地字体时,这可能会导致出现意外字符。为了避免这个问题,Paul Irish 提出了一个修改后的规则,让浏览器忽略local()引用,强制下载链接的字体:

@font-face {    font-family: "Lilly";    src: url("fonts/lilly.eot");    src: local("☺"), url("fonts/lilly.woff ") format ("woff"), url("fonts/lilly.ttf") format("truetype"), url("fonts/lilly.svg#LillyRegular") format("svg"); }

通过使用一个不太可能用作真正字体名称的字符,这种语法避免了用户在计算机上安装 FontExplorer X 等软件时可能出现的任何问题,但可能会强制进行不必要的下载。


6[openfontlibrary.org/wiki/Web_Font_linking_and_Cross-Origin_Resource_Sharing](http://openfontlibrary.org/wiki/Web_Font_linking_and_Cross-Origin_Resource_Sharing).阅读更多信息

images 注意:每个浏览器都有自己的问题列表,在使用 font-face 时需要绕过。Paul Irish 在两个帖子中记录了其中的一些,这两个帖子经常更新,并且在[paulirish.com/2009/bulletproof-font-face-implementation-syntax/](http://paulirish.com/2009/bulletproof-font-face-implementation-syntax/)[paulirish.com/2010/font-face-gotchas/](http://paulirish.com/2010/font-face-gotchas/)有几个开发人员的贡献。

虽然font-face允许在设计网站时有很大的灵活性,但在使用任何字体之前都需要考虑字体许可。还应该考虑带宽问题,因为字体文件往往很大,尽管这可以通过使用精简字符集和 gzip 字体文件来解决(WOFF 除外,它已经是一种压缩格式),以及字体非常可缓存的事实。

images 注意:如果需要支持多种语言,字体文件会很快变大。为了支持所有已知的语言,字体需要支持成千上万的字形。

许多在线服务现在为设计师提供了大型字体库,并使其易于嵌入网站中(见图 5-2 )。这可能是一种选择,但你需要记住,这些服务需要不断更新和付费订阅服务,你将依赖于别人的服务器和他们的正常运行时间。

images
images

图 5-2。 Typekit(上图)和 Fontdeck(下图)是两项在线服务,允许您在网站中使用 font-face,同时提供托管服务并确保所有字体都获得在线使用许可。

images 注意:为了保护自己的字体,TypeKit 使用了 JavaScript,这就产生了 JavaScript 依赖。Fontdeck 使用纯 CSS 解决方案来提供其字体。

像 Font Squirrel 这样的免费服务都有可以免费使用的字体库(见图 5-3 )。通常你必须下载真正的字体文件并自己保存它们。字体松鼠提供各种字体格式,以适应每种浏览器(EOT,WOFF,SVG 等)。

images

图 5-3。 Font Squirrel 有一个庞大的免费商业用字库。

使用自定义嵌入字体比使用图像更好,但是您需要做好在某些浏览器中失败的准备,并且您需要意识到法律可能不会使这成为您或您的组织的可行选项。它还有效地使字体公开可用,并且第三方很容易以他们认为合适的任何方式下载和使用这种字体,这是许多保护其品牌的公司不愿意看到的。

备用字体

CSS 作者必须预见到,不是每个用户都会在他们的系统上安装最佳字体。这包括确保样式表包括适当的备用字体,并验证网站的布局是否灵活,以适应字体大小的变化(一些字体可能比最佳字体大,如果不考虑这一点,可能会破坏布局)。

当选择备用字体时,不应该简单地根据它们是衬线字体还是无衬线字体来选择字体。您应该考虑一些因素,如备用字体的纵横比是否与首选字体相似。确定一种字体是否是合适的替代字体的一个简单方法是覆盖使用不同字体选项的单词,并检查其高度、宽度和粗细是否与最佳字体足够相似。你可以在 Photoshop 或 Fireworks 之类的程序中轻松做到这一点(见图 5-4 )。

images

图 5-4。如图所示,Chaparral Pro(深灰色)在 Times New Roman(右,浅灰色)中找到了比 Georgia(左,浅灰色)更好的退路,尽管并不完美。

在使用与font-face相关的技术时,比如前面提到的 Typekit 或 Fontdeck,确保备用字体与首选字体相似是非常重要的。当使用这些字体时,网页在下载和向浏览器提供正确的字体时通常会有延迟,在一小段时间内显示下一个可用的字体。当这种字体的大小和比例与由font-face服务提供的字体不同时,页面上的内容将跳转,文本流将重新排列,这可能会干扰用户,特别是如果他们的连接速度很慢,或者如果他们有阅读和学习障碍,如阅读障碍。这被称为无样式文本闪现(或 FOUT),无样式内容闪现(FOUC)的字体特定版本,当您可以看到网站的无样式 HTML 时,在 CSS 加载之前,或在 JavaScript 修改 DOM 之前发生。

属性可以最小化这个问题,不仅是当你使用服务和体验跳转的时候,也是当用户没有在系统中安装首选字体或者 ?? 不在的时候。如果使用一种备用字体,此属性允许您保留首选字体的纵横比。就拿下面这个 CSS 来说吧:

p {     font-family: Calibri, "Lucida Sans", Verdana, sans-serif;     font-size-adjust: 0.47; }

font-size-adjust值将除以备用字体的原始font-size值,保留备用字体中首选字体的 x 高度。在前面的例子中,如果使用 Lucida Sans、Verdana 或默认的 system sans serif 字体,为了保持 Calibri 的纵横比,它们的font-size将除以 0.47。

images 提示:一些推荐的字库可以在[www.awayback.com/revised-font-stack/](http://www.awayback.com/revised-font-stack/)找到。

单位

文本可以在网页上以各种不同的单位类型设置,关于旧像素 vs EMS7的争论已经说得很多了。以像素为单位设置文本的两个主要优势满足了设计人员和开发人员的需求:我们对设计有更多的控制权,并且我们不需要执行复杂的计算来确定字体大小。缺点完全在于用户的领域:一些浏览器不支持使用像素设置的文本的字体大小调整。由此,应该很容易推断出使用更灵活的单位(如 ems 或百分比)的利弊。(我们将在第六章的中深入探讨单位的主题。)

虽然大多数新浏览器(甚至是 Internet Explorer 7)允许用户放大(缩小)整个页面,但他或她可能只想放大文本。如果文本以像素为单位设置,一些浏览器(如 IE 6 到 9)会忽略这个用户设置。放大整个页面也可能导致浏览器在更宽的网站上显示水平栏,这可能相当令人讨厌,因为它以网站设计者没有考虑的方式限制了用户的视区,并且如果它导致内容被裁剪,可能会使内容变得无意义。数量惊人的人选择缩放或调整字体大小,可能是因为他们有某种视觉或认知障碍,或者是不寻常的屏幕尺寸或分辨率。

无论你决定在你的站点中使用像素、ems 还是其他单位度量,指南都需要反映这一点。这是设计师(或设计师团队)的工作,制作指南,以确定哪些是最好的规模和比例使用。指南应该在默认场景中使用像素,例如 BBC 的全球体验语言(GEL),其中字体比例和大小以像素表示,但网站的实际 CSS 使用 ems,因为它们是更容易访问的选择。


em 之所以被称为 em,是因为它被认为是大写字母“M”的高度。事实上,根据印刷工 Robert Bringhurst 的说法,“em 是一个滑动的量度。一个 em 是等于字体大小的距离。在 6 点字体中,em 是 6 点;在 12 点字体中,em 是 12 点,在 60 点字体中,em 是 60 点。因此,一个 em 空间在任何大小上都是成比例的相同。”

images

图 5-5。英国广播公司的全球体验语言指南以像素为单位说明了允许的字体大小,尽管英国广播公司的网站使用 ems 作为字体大小。

在 BBC 新闻主页上,头条新闻的h2级别标题的font-size值为 2.461em。这个数字可能看起来很奇怪,但是如果您查看最终计算的值(您可以使用 Firebug 来计算),您会发现它实际上是 32px(假设您的浏览器使用默认的字体大小)。开发商是如何得出这个数字的?body元素的字体大小设置为 62.5%。由于默认的字体大小通常是 16px,这个老技巧使得 em 计算更容易:16 的 62.5%是 10。一个 em 等于 10 个像素。因为页面包装器和标题的祖先应用了“font-size: 1.3em”(使其计算值为“13px”),所以我们需要计算 32px/13px,以了解我们必须增加多少子级的font-size,从而获得所需的 32px。听起来复杂吗?的确如此,这也是为什么尽管 ems 是一种更容易使用的解决方案,但是许多 CSS 作者都避免在他们的样式表中使用 EMS 作为字体大小单位。我们建议您花时间做这项工作,并在可能的情况下使用 ems。

images 提示:[topfunky.com/baseline-rhythm-calculator/](http://topfunky.com/baseline-rhythm-calculator/)[jameswhittaker.com/journal/em-based-layouts-vertical-rhythm-calculator](http://jameswhittaker.com/journal/em-based-layouts-vertical-rhythm-calculator)有几个不错的 em 计算器。在[fordinteractive.com/tools/emchart/](http://fordinteractive.com/tools/emchart/)也有一个方便的参考图表。

颜色

颜色是品牌被感知的一个基本因素。一些品牌与它们的颜色联系如此紧密,以至于有时我们甚至不需要看到它们的名字,就能从它们的颜色和形状中认出它们(例如,国家地理的黄金矩形或可口可乐的红色字体)。

正因为如此,每个品牌风格指南几乎肯定会包括一个与颜色相关的部分:可以使用什么样的调色板,合适的组合和比例是什么,每种颜色在品牌色彩景观中代表什么,等等。

有了这些指导原则,重要的是实现团队了解它们,知道如何使用颜色以及如何将它们转换到 Web 上——前端开发人员甚至不知道他们正在构建或扩充的网站上应该使用预定义的颜色,更不用说了解这些颜色的适当用途了。这导致了设计者和开发者的挫败感,也稀释了用户的品牌。

对于设计师来说,了解色彩在印刷品和网络上的作用是不同的也很重要,首先,尽管印刷品对每个人来说看起来都一样,但每个用户都用不同的显示器或设备浏览网络,具有不同的功能、校准和配置。在开发风格指南时,网页设计师(和平面设计师一样)应该考虑到有视觉障碍或色盲的人,并确保设计中包含的颜色和颜色组合提供了适当的对比度,但这些问题属于易访问性范围,而不是本章的主题,因此将在第六章中进一步讨论。

品牌指导方针是首先考虑网络色彩,然后是印刷色彩,还是反过来是设计师的选择;只要有一个在网络上使用的定义好的和同意的调色板。

然而,一些视觉品牌指南并没有提及屏幕颜色。这可能是因为该指南是在互联网还没有被考虑的时候制作的,或者仅仅是因为它不完整。无论是哪种情况,都应该修改指南,以包括在 Web 上使用的适当的颜色参考,避免时间浪费和不精确的颜色使用,因为每个 CSS 作者都必须将 CMYK 和 Pantone 颜色转换为 RGBA、十六进制或网站的首选颜色格式, 8 ,并且这个过程应该由设计师或设计团队签署。

多种配色方案

颜色是区分子网站或网站部分的有力工具。例如,如果你有一个面向儿童的子品牌,它可能会用更大胆、更明亮的颜色设计,而如果你的组织中有一个高级品牌,你可能会使用更经典、更优雅的颜色。

当构建子品牌和他们的网站时,从用户和开发者的角度来看,更容易保持网站范围内的一致性,同时保持差异清晰——我们希望他们知道他们仍然在我们的网站大家庭中,但看的是不同的产品。

Fox 是一个很好的例子,通过使用相同的基本模板,你可以有几乎无限范围的不同子网站,在这个例子中是每个节目。在 Fox 的例子中,加粗的背景图像与特定的节目相关,而链接配色方案遵循该节目的品牌。然而,页面布局和导航元素的设计和行为保持不变。这很好地展示了如何实现外观的变化,同时保持设计模板的一致性,借助颜色(和图像)进行区分,避免不和谐和不一致的用户体验。


8 CMYK、RGB 和 Pantone 颜色具有不同的色域(颜色子集),一种颜色模型中的一些颜色在其他颜色模型中“超出色域”(无法准确再现)。在此阅读更多关于色域的信息:[en.wikipedia.org/wiki/Gamut](http://en.wikipedia.org/wiki/Gamut).

images

图 5-6。福克斯的《美国爸爸》和《厨艺大师》网站:这两个例子展示了如何通过使用相同的模板和不同的配色方案和图像来实现差异化。

需要记住的一点是,用户很少按照颜色对网站的各个部分进行分组,也很少理解其意义(尽管组织内部的人和开发人员可能会理解),除非颜色已经与品牌相关联。这里提到的案例就是这样的例子;给你公司的各个部门标上颜色是完全不同的事情,因为他们不会被外界视为“品牌”

颜色参考

除了生活在视觉样式指南中,在 CSS 文件本身中有一个调色板参考也很有用。在第二章第一部分中,我们展示了如何将它们包含在 CSS 注释中,这样对于作者来说总是有一个方便的参考。下面是一个如何在 CSS 文件顶部保留调色板的示例:

`/* Color reference:

Main text    #333333
Background   #f7f7f7
:link,
:visited     #dd4814
:hover,
:active,
:focus       #bc522d

*/`

您可以选择使用 CSSDOC 注释;你可以在第二章的中看到更详细的解释。这种方法要求您在两个不同的地方维护信息,尽管将这些引用保存在 CSS 文件中很方便,但您可能更喜欢更准确的单个引用点。

动态色彩

使用动态 CSS 或编译器,如 LESS 或 Sass,大型网站上的颜色和颜色更新可以变得更容易管理。使用这些技术,您可以轻松地为最常用的颜色(如主文本、链接、背景或边框)设置变量,无需经常参考颜色指南来使用正确的十六进制代码或 RGB 参考。您甚至可以基于三种或四种主色生成整个配色方案,方法是将其他颜色基于这些主色并使用颜色计算。我们将在第九章中更详细地介绍这个主题。

回退颜色

旧的浏览器不支持新的 CSS3 颜色格式,如 RGBA 或 HSLA。对于这些浏览器,您必须始终确保提供后备颜色。例如,如果在一个div上指定一个透明的背景颜色,你可以在 RGBA 浏览器之前提供一个声明(这个声明将被忽略,因为它不被支持):

div {    background-color: #b9cf6a;    background-color: rgba(255, 255, 255, 0.3); }

在前面的例子中,我们没有指定准确的十六进制版本 255,255,255,因为那将是纯白色的。相反,使用拾色器工具(比如 xScope [iconfactory.com/software/xscope](http://iconfactory.com/software/xscope))你可以选择一种近似透明色和它下面的基本色的颜色,给老的浏览器一个更精确的设计。这些备用颜色不太可能在品牌指南中指定,所以确保它们存在并被准确选择是一个勤奋和彻底的 CSS 作者的工作。安全总比后悔好,所以留个评论提醒自己更新回退颜色,以防原始颜色发生变化总是个好主意。

当使用 CSS 渐变时,记住总是指定一个后备背景颜色,确保在不支持它们的浏览器上,背景和前景之间仍然有足够的对比度。同样适用于使用图像作为背景色(确切地说,CSS 渐变实际上是动态生成的背景图像和而不是颜色);始终为容器指定背景颜色,并始终在关闭图像并打开 CSS 的情况下测试网站——对于在禁用图像的情况下浏览(在较慢的连接或移动设备上)的用户来说,由于开发人员忘记定义背景颜色并依赖背景图像进行对比而无法访问内容可能会非常令人沮丧。慢速连接的用户也会看到你的网站在加载之前图像丢失了一会儿,而且屏幕变化越小,发生时对用户的干扰就越小。

布局

定义页面的基本结构是 CSS 网站编码的一个重要步骤,它将影响到整个过程。重要的是要有一个坚实的基础——一个定义好的网格,每个单独的页面都可以建立在这个网格上,而不是在需要的时候就把“s”扔到页面上。

网格和模板的使用也将提高 CSS 文件和设计的灵活性(和适应性),有助于保持布局的一致性。

网格

知道如何最好地使用网格是任何一个设计师的基本技能,无论是在网上还是网外。

然而,使用网格并不意味着创建僵硬的布局——它们是设计基础的一部分,有助于形成一个坚实的、考虑周全的结构,并有助于做出一些决定;但是,就像任何一套规则和约束一样,它们也会被打破。

Web 上一些最原始的布局是在适当定义的网格上组成的。我们来看一个例子(见图 5-7 )。

images
images

图 5-7。《连线》网站(上图)与网格叠加(下图)[www.wired.com/](http://www.wired.com/)

正如你所看到的,即使它遵循一个网格,Wired 网站的设计也不觉得死板。反而感觉很有表现力,很有动感。

一个仔细考虑过的网格是灵活的,它适合于多种布局设计,有助于保持不同页面之间的一致性,并且它应该有助于保持品牌的一致性。然而,当网格最终确定时,设计者的工作还没有完成;如果一个好的网格的唯一目的是正确对齐页面上的每一个元素,那么它将毫无用处。

虽然正确的对齐是实现平衡与和谐的基本步骤,但设计的其他方面并不包括在内——元素的视觉层次、颜色、排版等等仍然需要考虑。设计不仅仅是在网格中定位元素。

试图将网格转换成 HTML 和 CSS 的一个缺点是,内容的自然流动(文档的轮廓)可能会被忽略,成为网格的附属。当布局开始表现得更像基于表格的旧布局时,就会发生这种情况。

尽管基于表格的网站在使用立体网格时几乎是完美的,但它们的一个(许多)缺点是形式不符合内容;内容遵循形式。内容在页面上的视觉位置比内容本身的意义和流动更重要。如果 HTML 基本上是创建要定位的框,就像表格一样,那么我们没有做到内容公正。

这是许多 CSS 框架采用的方法,也是许多 CSS 作者不喜欢使用它们的原因之一。

在图 5-8 中,我们有一个基本的 8 列网格;每列宽 100 像素,左右各 10 像素。

images

图 5-8。一个简单的 8 列网格的例子

编写这种布局的第一反应可能是关注列并创建八个浮动在左侧的容器:

div {    width: 100px;    float: left;    margin: 0 10px; }

但是更有可能的是一个网格中的元素跨越了不止一列,如图 5-9 所示。

images

图 5-9。带有内容块的 8 列网格

这个网格可以用许多不同的方法转换成 HTML 和 CSS,但是最佳的方法总是考虑到页面的实际内容和流程,并且保持顺序不变。网格是次要的,内容不应该成为它的从属;它仅仅是实现布局一致性和和谐性的一种手段。

在我们的示例中,我们可以从定义最重要的内容块开始,并从底层网格中提取它们的宽度:

`header {
   width: 940px; /* full width /
}
nav {
   width: 220px; /
2*100px + 1x20px */
   float: left;
}

highlight {

width: 580px; /* 5*100px + 4x20px */
   float: left;
}

aside {
   width: 100px; /* single column = 100px */
   float: right;
}`

网格的存在是为了给设计者和 CSS 作者提供衡量标准,他们将在此基础上做出一些设计决策,这将使从设计阶段到编码阶段的翻译不那么容易带有主观性。如果负责 CSS 的人知道设计中跨越两列的元素自动为 220 像素宽,这将有助于设计保持一致性和平衡。

我们将在第十一章中更详细地介绍使用网格的过程,在那里我们将从头开始创建一个框架。

模板

HTML 和 CSS 模板为作者提供了一个坚实的基础。模板是确保标题、导航、基本页面结构和版式等关键元素在一系列网站和子网站中保持一致的简单方法。

模板和 CSS 框架之间有一些不同,尽管它们的目的是交织在一起的。现有的 CSS 框架由预先构建的代码库组成,您可以根据手头的项目进行调整。有几个现存的 CSS 框架可供许多开发者和机构免费使用(我们将在第四章中介绍),比如 960gs 或 Blueprint。在某些情况下,团队创建他们自己的 CSS 框架(我们强烈推荐这种方法,并在第十一章中介绍)。框架应该具有适应性、灵活性和模块化。

模板可以采取不同的方法。您可能有一个 HTML 和 CSS 模板,其中包括一个示例网站的页眉、主导航、内容区域和页脚,并且应该在您组织内的所有网站中使用。然而,这并不意味着这组文件允许框架所做的模块化和通用的方法。

模板是标记应该如何构造以及 CSS 如何绑定到它的基本示例——它们作为页面的框架而存在。理想的情况是基于现有的 CSS 框架构建 HTML 和 CSS 模板,这样开发人员就可以看到 CSS 文件如何被使用、覆盖和适应各种页面模板的活生生的例子。

主题

组织通常会根据相关的季节、假期和事件来改变网站的设计和内容,例如夏天的开始、圣诞节、新年或促销活动。

这些更新通常是显示网站没有停滞不前的好方法;它是不断更新的;最重要的是,背后的公司、产品或服务关心他们的客户做的同样的事情——这增加了参与度。

虽然有时品牌重塑可能只与一次性事件有关(例如,公司的 25 周年纪念),但重复发生的事件也会引发这些更新(例如,每年一次,如圣诞节品牌重塑,或一年几次,如季末销售活动)。

季节性品牌重塑的最好例子之一是星巴克的圣诞节网站。除了重新装修他们在世界各地的商店以符合季节精神,星巴克还为他们的网站访问者提供了一个改进的设计,与商店和广告活动相协调,在完美的营销编排中(见图 5-10 )。

images

图 5-10。星巴克的圣诞主题网站有了全新的设计和布局。

从设计的角度来看,设计人员的工作是处理更复杂的站点主题的创建,但是这些改变最终会落入 CSS 作者和其他开发人员的手中,他们需要能够用更新来覆盖现有的样式表。

这可以被看作是对 CSS 的一次考验:样式表需要足够灵活,这样覆盖一些规则就不会变成特定性的噩梦。

images 注:注意到在本书中特异性噩梦这个术语被提及的频率了吗?这是因为我们对它太熟悉了,因为这是 CSS 作者经常面临的情况——非灵活和脆弱的样式表的结果。

如果 CSS 使用了过于特殊的选择器,当创建一个新的主题时,你将不得不创建更多特殊的选择器。理想情况下,您应该能够向body元素添加一个类或 ID,例如“christmas ”,或者在 HTML 的header部分的默认样式表之后链接季节性样式表,或者将新规则合并到现有的主文件中。

例如,Threadless 在body标签中添加了一个类,这样网站的某些元素就可以被定位,它们的 CSS 也可以随着改变而被覆盖(见图 5-11 )。

`.holiday_week_2_3 .footer_wrap {
    background: url(/imgs/sale/black_friday/bg_footer.jpg) no-repeat scroll center 30px

ffffff;

clear: both;
    overflow: hidden;
    padding: 90px 0 0;
}` images

图 5-11。以销售为主题设计的无线网站。

你可以在第四章中阅读更多关于覆盖 CSS 文件的内容。

灵活的 CSS 和品牌进化

品牌不会永远不变。尽管最知名的品牌倾向于避免激进的品牌重塑,这可能会损害客户和公众对他们的看法,但我们也经常看到他们进行较小的更新,这些更新在外部几乎察觉不到,但仍然涉及到 web 实现团队的一些工作。

这些小的变化可能包括排版的改进、颜色的改变、整个网站使用的图像的更新、较小的布局和导航调整以及徽标替换等。

大公司每两三年更新一次他们的网站也并不少见。这可能有多种原因:整体品牌重新设计需要反映在网站上,网站看起来过时(这种情况尤其发生在它被设计成遵循迅速过时的网页设计趋势的情况下),公司被重组,或者网站本身的内容被重组,等等。

甚至那些倾向于保持不变的品牌也会对他们的网站做一些小的改动,主要是因为媒体的快速变化的本质,也因为这些改动通常比,例如,重新装饰整个车队或为数千名员工重印文具的成本要低。

因为进行这些更改通常会有成本,而速度总是人们追求的目标,所以通过创建灵活的 CSS 文档来为这些更改做准备是很重要的,这些文档可以尽可能容易地更新以进行小的或大的调整。

这是本书的主旨:让你的网站的 CSS 具有适应性,容易被很多人使用,并且在需要的时候可以更新,而不破坏其他的东西。

当网站面临大规模更新或重新设计时,重要的是尽早决定是从头开始更好,还是现有的 CSS 灵活且易于适应变化。通常,如果底层标记被重做,这也意味着 CSS 需要被重新构造,因为标记的构建方式往往与 CSS 的组织方式直接相关。

然而,最常见的场景(以及那些使大型网站必须拥有专门的 web 团队的场景)与不太明显的持续更新有关,这些更新虽然很小,但会持续测试标记和 CSS 的健壮性。这可能是向主导航添加新链接、更新文本颜色以使其更易于访问、重新调整网格、创建新的侧边栏小部件,或者为不同语言的新版本准备站点——在任何 web 实现团队中常见的日常任务。

这些都是很好的例子,说明了为什么保持 CSS 的一致性和灵活性很重要,以及最新和详细的 CSS 和设计和品牌风格指南如何使实现新元素或扩展现有元素变得更容易。

总结

CSS 作者和前端开发人员的大部分工作是让设计在网络上变得生动。用户体验设计师、信息架构师、视觉和图形设计师将他们的知识和最大努力投入到创建独特的品牌和产品中。确保他们的工作执行得天衣无缝是至关重要的,因为这将影响您的组织在外部(和内部)的形象,并最终影响其成功。从事网络工作的专业人员需要考虑到许多方面,我们假设他们知道这种媒体的潜力,但也知道它的局限性。建立清晰的品牌和设计指导方针,确保每个人都知道并理解它们,并有一个建立设计规则但允许灵活性和效率的过程是每个大型网站团队应该努力的,也是本章应该帮助实现的。

在下一章,我们将讨论可访问性以及它如何影响你的 CSS。我们将关注可能影响我们用户的缺陷,以及您可以采取哪些措施来确保他们获得最佳体验。

六、CSS 和可访问性

从本质上说,使网站易于访问意味着确保每个人都可以访问所有内容。这并不意味着每个人都必须有相同的体验,但这确实意味着不应该有任何人无法实现(或无法实现)的功能或内容,无论他们的个人能力、设备、浏览器或环境如何。

可访问性不是核对需求列表,或者使用自动化工具来验证我们的代码是否通过了特定的可访问性指南和验证。虽然法律可能要求您的组织通过特定的可访问性级别(不同的国家有不同的可访问性法律),但在开发高流量网站时,确保您的网站可以被尽可能多的设备和用户访问应该是您主要关注的问题之一。这是你应该为你的用户、你的品牌以及他们对它的好感所做的事情。不违法可能会被视为意外收获。

关于网上可访问性,可以说(也已经说了)很多,足以写满一整本书,甚至一本集子。建立易访问的网站在很大程度上是,但不仅仅是,建立语义网站的副产品。然而,这并不仅仅是开发人员的责任。与网站相关的每个人都应该参与进来:用户体验设计师应该确保网站易于导航,内容易于查找;设计师应该设计出考虑残疾用户的布局,比如色盲或运动技能差的用户;产品经理和广告文案应该创造简单明了的信息;开发人员应该编写具有语义和可访问性的代码。有几个考虑因素,尤其是高流量的网站。此外,有许多与 HTML 相关和与内容相关的操作可以让您的内容更容易被访问,但是作为一本与 CSS 相关的书,它们超出了本文的范围。我们会给你一些建议,告诉你如何向尽可能多的访问者和客户开放你的网站和(最重要的)你的内容。 1

在本章中,我们将了解

  • 一些最常见的用户可访问性问题概述
  • 现有的可访问性指南以及如何满足这些指南
  • 如何让访问者使用辅助技术更容易
  • 更易访问的站点的设计和布局考虑
  • WAI-ARIA 以及如何在你的项目中使用它
  • 针对有设备或环境障碍的用户的 CSS 最佳实践
  • 渐进增强和适度退化的区别
  • 如何使用雅虎!分级浏览器支持

这不仅是一件有益的、无私的事情,也是一件有利可图的事情。英国超市 Tesco 与皇家国家盲人学院(RNIB)合作,确保其网站无障碍。这导致了一年 1300 万英镑的额外收入,这在以前是不可能的。

减值问题概述

什么被认为是,什么不被认为是损害是一件很难以政治正确的方式讨论的事情。一些人认为这是一种残疾,另一些人认为这只是人与人之间的差异。当我们谈到缺陷时,我们指的是使用户使用网站变得更加困难的东西。一些最常见的无障碍障碍如下:

  • 视力低下、视力不佳或失明
  • 不同种类的色盲
  • 运动障碍或运动技能受限
  • 听力障碍
  • 认知和智力残疾
  • 年轻,这可能意味着认知障碍
  • 年老,这可能意味着这里列出的许多问题

比你想象的更常见的是,人们会有不止一种情况,这使得满足每种可能的组合更加困难。

例如,那些聋哑人通过不同的方式交流和吸收信息。那些最初失聪然后在以后的生活中失去视力的人很可能使用他们已经知道的手语的修改版本,以触觉的形式。而那些生来失明,然后变聋的人很可能使用他们的书面或口头语言的触觉版本。即使是像耳聋这样的单一障碍,也可能有数十种(甚至数百种)解决方案(例如,唇读、手语、高级手语、助听器等等)。有比我们在这里可能进入的更多的组合。需要记住的一件重要事情是,这些用户已经决定了他们想要和确实喜欢如何使用他们的设备,并且已经准备好应用程序来帮助他们实现这一点。

我们不是为残障人士服务的专家,也不能指望成为专家,但我们可以尽最大努力不干扰用户选择的方法。这同样适用于每个人——如果一个用户定义了键盘快捷键,而我们未经允许就覆盖了它们,这对用户来说是令人沮丧的,对我们来说是放肆的。如果另一个用户试图右键点击一个页面,而我们取消了这个行为,同样,我们这样做是不礼貌的。让浏览器和设备有机会做他们认为合适的事情,通常是我们应该做的最礼貌和最恰当的事情。

让我们来看看有特殊障碍的人可能面临的一些更常见的问题。显然,这不是一份详尽的清单。我们将在本章的后面更详细地讨论每一个问题。

部分失明

部分失明(但不是失明)的用户属于两个主要群体之一。那些难以集中注意力的人将受益于清晰、简单、中等粗细的字体和大字体。 2 允许他们在你的网站内改变字体大小和字体的能力会让他们的生活更轻松。确保您使用字体大小的相对值也将让他们使用内置的设备方法来调整字体大小,并避免!重要的声明将有助于确保您不会覆盖他们自己所做的任何设置。


2[www.rnib.org.uk/professionals/accessibleinformation/text/Pages/fonts.aspx](http://www.rnib.org.uk/professionals/accessibleinformation/text/Pages/fonts.aspx).阅读 RNIB 对字体的评论

视野狭窄的人(一次只能专注于屏幕上的小区域)将受益于调低字体大小的功能,这样段落在屏幕上占用的空间就更少了。这些用户更有可能把整个网站缩小,但每个人都不一样。您应该始终确保您已经测试了站点,以确定您可以在浏览器中将字体大小调高和调低两步,而不会破坏布局。

失明

盲人用户很可能使用屏幕阅读器(我们将在本章后面的专门章节中提到)。他们浏览网站的方式也与视力正常的用户非常不同。他们可能希望直接跳到网站的各个部分,访问页面中所有链接的列表,或者访问由标记中的标题构成的页面轮廓。避免使用指向页面上特定物理位置的语言(例如,“点击右上角的链接”),使用 WAI-ARIA、语义和微格式。更新的技术将能够利用这些优势。

色盲

色盲用户可以使用他们自己的样式表或者完全关闭 CSS。向他们提供各种可供选择的配色方案对他们有很大的帮助,可以让你的组织保持对其品牌的控制,并确保你的网站保持吸引力。在本章的后面,我们会引用一些工具来模拟色盲并检查你的配色方案的问题。

运动障碍

运动障碍的程度会有很大的不同。在大多数情况下,这些用户已经在使用他们自己的辅助技术来使他们能够使用他们的设备,但是我们可以做一些小事来帮助他们,比如使可点击的区域尽可能清晰和大。只要有可能,确保你链接的目标区域不需要很精确的点击。 3 这些考虑也适用于智能手机或平板电脑等触摸界面的用户。

无论如何,在许多情况下(比如标签页或按钮),不把可点击区域做成容器大小是一种令人困惑的用户体验。

听力障碍

除了特定的音频或视频内容之外,部分或完全失聪的用户在互联网上一般较少遇到可访问性问题。大部分网站没有(或者肯定不应该!)播放音频, 4 这样对听障人士和其他用户一样有效。您应该避免单独使用声音来表示页面上的任何状态或信息。如果您的网站将音频或视频作为其主要目的的一部分,您需要考虑为这些媒体添加字幕。

开放式字幕是“烧录”到与其相关的媒体中的字幕;比如电影中无法关闭的字幕。它被称为“打开”,因为默认情况下它是显示的。 隐藏式字幕是与媒体相关联的文本数据,以文本格式存储并与视频一起提供。例如,长期以来,法律要求电视提供隐藏式字幕解码器。隐藏式字幕的好处是允许几个不同的文本“轨道”;例如,不同的语言或声音的描述以及对话。它可以与音频一起用作卡拉 ok 的歌词,因为它是时间编码的。它还有一个好处,就是允许我们用 CSS 潜在的样式化文本(不同的位置来避免重要的视频元素,颜色来代表不同的人,等等)。


3 费茨定律([en.wikipedia.org/wiki/Fitts's_law](http://en.wikipedia.org/wiki/Fitts's_law))预测快速移动到目标区域并点击它所需的时间取决于移动的距离和目标的大小。更大的目标让每个人更有效率。

网站当然不应该自动播放音频,因为这会让用户感到不舒服和困惑。

遗憾的是,到目前为止,隐藏式字幕在网络上和许多不同的解决方案中都没有得到很好的支持。HTML5 还没有对隐藏式字幕的官方支持,但是已经被证实的方法正在被实现,标准化肯定会随之而来。 5

目前,隐藏式字幕的 CSS 考虑在很大程度上取决于您的实现,而脚本是公开这些数据的唯一万无一失的方法。你可以在[webaim.org/techniques/captions/](http://webaim.org/techniques/captions/)阅读更多关于字幕的信息。

同样重要的是要意识到,那些生来失聪的人很可能已经学会了手语作为他们的第一语言。这意味着英语(或他们的当地语言)对他们来说类似于第二语言,因此表达方式、行话和俗语对他们来说可能更难理解。因此,清晰的语言和以往一样重要。

认知障碍

认知障碍,如阅读障碍或自闭症,会使用户难以处理信息。一般来说,这种缺陷可以通过清晰明确的文案来解决,但你也可以做其他事情。保持布局的一致性,保持导航等关键元素的位置,会让所有用户的生活更轻松,尤其是那些有认知障碍或学习障碍的用户。这同样适用于不要使用太多不同的字体(尽管网站应该足够灵活,以便用户可以很容易地指定他们喜欢的字体)。此外,确保行与行之间有足够的空间,因为这使得文本更容易阅读(对于普通文本,1.5 的line-height值是一个很好的默认值)。

诵读困难是一种非常常见的障碍,尽管没有进行过精确的研究,但据信诵读困难影响了 5%到 10%的人口。诵读困难症患者可能会遭受各种各样的学习或认知障碍,这就是为什么当提到无障碍时,这个特殊的问题被如此频繁地提及。一些诵读困难者更喜欢打印页面,而不是阅读屏幕上的文本。这只是为什么有人会打印一页纸的各种原因之一。

如果你没有开发一个专门针对阅读障碍者的网站,那就没有必要证明你的网站的每一个细节都是阅读障碍友好的,但是你应该记住,在这种情况下最重要的因素是用户可以定制他们访问的页面,以满足他们非常特殊的需求,使其更容易阅读和理解内容。


5Web Video Text Tracks(Web vtt),以前被称为 WebSRT(基于流行的 SRT (SubRip Text)格式),目前正在由几个浏览器实现,因此是目前最有可能被标准化的提案。你可以在[www.whatwg.org/specs/web-apps/currentwork/webvtt.html](http://www.whatwg.org/specs/web-apps/currentwork/webvtt.html).阅读

年轻时代

年轻用户和其他人一样可能遭受列表中的许多其他障碍,但他们也可以被认为有一种认知障碍,因为你应该使用简单、清晰的语言。为儿童设计的网站也是使用漫画无字体的唯一有效的地方,尽管一些阅读障碍者也可能会发现它更容易阅读。

老年

老年用户可能会遭受列表中的任何一种或多种损伤(当然,除了前面提到的那种)。特别是视力和听力会随着年龄的增长而退化。因此,如果你确定你的网站是可访问的,你可能已经设计了一个老用户可以访问的网站。他们也可能对计算机不熟悉或不熟练,所以避免技术术语,使用常见的视觉联想效果很好。

癫痫

癫痫症患者尤其会对闪烁的页面或页面中的元素产生不良反应。许多国家/地区的法律规定,在展示此类媒体之前必须发出警告。避免动画或无样式内容的闪烁(FOUC)。

images 提示:BBC 有一个很棒的资源,可以帮助有不同无障碍问题的人了解他们需要知道的东西,让互联网对他们更友好。虽然在撰写本文时它仍处于测试阶段,但您可以在[www.bbc.co.uk/accessibility/accessibility_beta/](http://www.bbc.co.uk/accessibility/accessibility_beta/)访问它。

易访问性问题不一定意味着用户的身体或心理问题。这可能仅仅意味着他们用来访问你的网站的设备或环境的能力比预期的要低。

让我们来看看一系列可能转化为非生理性无障碍障碍的常见问题:

  • 旧浏览器
  • 拨号或慢速互联网连接
  • 小型或低保真度屏幕
  • 触摸屏
  • 无法使用鼠标或键盘
  • 缓慢的处理器或旧电脑
  • 眼睛、手或耳朵可能被占用的环境(例如,锻炼或驾驶)
  • 有意禁用 JavaScript、CSS、图像或 cookies

没有确切的数字可以告诉我们有多少用户可能属于这些类别。在许多情况下,它们可能是暂时的损伤(他们可能在旅途中试图验证一些信息,或者使用他人或其工作的计算机)。但是,如果我们只考虑第一个问题,看看有多少用户仍在使用旧浏览器浏览网页的统计数据,虽然这对许多低流量网站来说并不重要,但在高流量网站上,即使很小的百分比数字也可以转化为每天数万的访问者。此外,IE 6 的使用可能已经下降到全球所有互联网流量的 4%或 5%左右,但它仍然占非洲流量的 12%以上。如果你的网站获得了世界范围内的流量,看世界范围的统计数据而不是按国家进行分类会产生误导。 6

你可以在本章后面阅读更多关于如何帮助遭受设备和环境损害的用户。

images 提示:意识到用户在某个特定时间可能面临的所有可访问性问题并不意味着我们应该以最小公分母来构建我们的网站。这意味着我们应该确保内容——我们网站的核心——对每个人都是可用的。

无障碍指南

有许多可访问性指南和清单。Web Accessibility Initiative(WAI[www.w3.org/WAI/about.html](http://www.w3.org/WAI/about.html))是 W3C 的领域之一。他们有几个工作组,但最常引用和应用的是网页内容可访问性指南(WCAG [www.w3.org/TR/WCAG10/full-checklist.html](http://www.w3.org/TR/WCAG10/full-checklist.html))。这个清单——不像 W3C 的许多其他文档——相对简洁,非常容易阅读。该页面设计用于打印和用作实际的清单。

考虑事项分为优先级 1优先级 2优先级 3 。优先级 1 项目被认为是可访问网站的基本要素。优先 2 物品强烈推荐,优先 3 物品有用。你可以分别考虑这些必须应该可能

实质上,为了符合优先级 1 项目,您必须执行以下操作:

  • 为页面上的每个图形元素提供等同的文本
  • 清楚地提供通过其他方式(例如,通过颜色或位置)表示的信息的文本对等物
  • 确保页面在禁用 CSS、JavaScript 或图像的情况下正常工作
  • 使用简单明了的语言
  • 避免闪烁
  • 如果所有这些都失败了,就链接到一个遵循指南的页面的替代版本

还有很多额外的和更好的地方。我们强烈建议您详细阅读该页面,并尽可能遵循合理的建议。一些国家,如加拿大或瑞典,遵循他们自己的可访问性指南。 7


6 有关浏览器使用情况的更多统计信息,请访问[gs.statcounter.com/](http://gs.statcounter.com/)[www.w3counter.com/globalstats.php](http://www.w3counter.com/globalstats.php).

7 加拿大有“通用外观和感觉标准”([www.tbs-sct.gc.ca/clf2-nsi2/](http://www.tbs-sct.gc.ca/clf2-nsi2/)),政府机构必须遵守该标准,包括关于网址、可访问性和可用性、网页格式和电子邮件的标准。现已消亡的瑞典行政发展署于 2006 年发布了《瑞典公共部门网站国家指南》;该指南一直由私人维护,并于 2008 年翻译成英文。它们涵盖了可用性、隐私问题、信息架构以及网络标准等领域。

什么规律?

不同的国家有不同的法律来规范可访问性。通常他们倾向于不仅仅与网络有关,而是反对对残疾人的歧视和促进平等。

在联合王国,1995 年的《残疾歧视法》也提到了网站。《行为守则》的重要内容是这样的:《残疾歧视法》规定,服务提供者拒绝向公众提供任何服务,从而歧视残疾人是非法的。

皇家国家盲人研究所(RNIB)和残疾人权利委员会(DRC)已经推动就《残疾歧视法》采取法律行动。RNIB 已经联系了两家大型网站,要求它们做出改变,以避免法律诉讼并保持匿名。DRC 调查了 1000 个网站,发现超过 80%的网站与《残疾歧视法》的要求相冲突。这些网站受到无限赔偿和法律诉讼的威胁。

在美国,《美国残疾人法》也是为了同样的目的。它不仅适用于网站,也适用于任何服务,包括建筑、医疗、电话等等。

消费者协会起诉大型组织的无障碍标准很差并不罕见,这些标准将成千上万的残疾用户排除在他们的网站和内容之外。原告在这些案件中赢得大笔金钱是有先例的。对于高流量和高知名度的网站来说,这是一个更真实的威胁,不应该掉以轻心。

在法律诉讼中,很可能会使用 WCAG 准则。欧盟建议的合规级别为优先级 2 。我们建议尽量满足优先级 2 的最低要求,并尽可能努力满足优先级 3 的要求。 8

辅助技术

有各种类型的残疾人辅助技术。辅助技术的主要目标是帮助残疾人在日常生活中更加自主。

经常应用于使用计算机(以及随之而来的互联网)的一些辅助技术是屏幕阅读器、屏幕放大器、语音识别软件以及盲文显示器和键盘。

我们将主要关注屏幕阅读器和仅使用键盘的用户,因为这些情况下的许多指南也适用于其他辅助技术,并且它们代表了残疾人使用的辅助技术的很大一部分。

屏幕阅读器

屏幕阅读器并不是视觉障碍用户的专属工具。患有其他类型残疾的用户在屏幕阅读器中找到了一种更有效、更可忍受的浏览互联网的方式(例如,当疲劳时,发现难以集中注意力,或忙于做其他事情),这种方式以线性方式呈现内容,而不会强加给他们可能使他们更加焦虑、更难理解内容的行为。这提高了潜在屏幕阅读器用户的百分比。


2006 年,美国盲人联合会对美国最大的零售公司之一塔吉特百货公司提起诉讼,指控其网站设置障碍,使盲人顾客难以使用网站。2008 年,目标定为 600 万美元。其他的例子包括 2000 年一个盲人成功起诉悉尼奥组委,以及纽约州起诉 Ramada.com 和 Priceline.com 的案件。

像浏览器一样,屏幕阅读器接收更新,并且每个更新都增加了对新功能的支持。而且,就像浏览器一样,用户不一定要更新他们的屏幕阅读器软件。其中一个主要原因是成本。屏幕阅读器的构建并不简单,通常是针对利基市场的。因此,它们通常非常昂贵。屏幕阅读器的功能和行为有很大的不同。VoiceOver ( [www.apple.com/accessibility/voiceover/](http://www.apple.com/accessibility/voiceover/))是一款苹果屏幕阅读器,作为 OS X 的一部分是免费的,功能齐全。如果您正在使用 Mac,我们建议您尝试一下,向您介绍一些您的用户将会面临的问题。这是一次真正有启发性的经历。按下 Command-F5 随时打开或关闭 VoiceOver。自 Windows 2000 以来,Windows 还包括了它的基本屏幕阅读器 Windows 讲述人。

屏幕阅读器提供了与网站交互的方法,这些方法不同于视力正常的用户使用的方法。VoiceOver 将此称为 Web 项目转子。这使得用户可以调出链接、标题等列表,并直接导航到它们,而不是被迫以线性方式在网站中导航,直到找到他们想要的内容。这很快暴露了许多明显的可访问性问题。VoiceOver 还会直观地呈现它大声朗读的内容。图 6-1 、 6-2 和 6-3 显示了一些例子。

images

图 6-1。维基百科为用户提供了合理的链接,尽管有 306 个。

images

图 6-2。 CNN 有一个合理的标题列表,尽管标题级别令人困惑。

images

图 6-3。麦当劳的链接只使用图片,没有标题属性。情况越来越糟,但让我们继续前进。

处理屏幕阅读器有一些 CSS 作者应该考虑的因素。

  • 如果您的布局以线性阅读时可能没有意义的顺序呈现内容,请在标记中以正确的顺序放置它,并使用 CSS 按照您喜欢的方式重新排列内容。
  • 如果隐藏内容并使用:hover伪类来显示它,对:focus伪类做同样的事情。这些用户不太可能使用指点设备,如跟踪板或鼠标。
  • 在页面顶部列出“跳过链接”的列表,链接到页面的主要部分,如导航、内容等。将它们放置在页面之外,使它们在焦点上可见。这可以确保你的主链接出现在页面的最前面,并且对所有用户都有用。
  • 给你的出版商一种通过 CSS 隐藏内容的方法(见下一节),这样看似“阅读更多”的链接实际上可以对屏幕阅读器用户说“阅读更多关于 XYZ 的故事”。
  • 当以不寻常的形式显示文本时(例如,全部大写),通过text-transform CSS 属性进行这种转换,因为屏幕阅读器可能会以不可预知的方式读取大写的 HTML 文本,比如将它们拼写出来或大声喊出来。
  • 屏幕阅读器通常不会读出通过 CSS 插入的内容,所以要确保你以这种方式添加的内容纯粹是装饰性的。

所有这些技术将有助于你的屏幕阅读器用户,也有助于搜索引擎和未来其他意想不到的设备的用户!要做一个基本的测试,试着关闭你的 CSS 并验证内容仍然有意义。然而,我们不能低估使用屏幕阅读器的价值;你会遇到的一些问题是如此容易解决,如此明显,你会踢自己,你的用户会喜欢你解决它们。

speech和(已弃用)aural媒体类型旨在帮助您将 CSS 明确定位于屏幕阅读器。我们建议您避免这种技术有三个原因。

  • 这些媒体类型没有得到屏幕阅读器的广泛支持。不能保证它们会被应用。此外,这些样式表将由非屏幕阅读器设备下载,无论如何都会导致性能下降。
  • 他们给我们的额外 CSS 功能,比如改变声音、音量、暂停和混音,都有可能覆盖用户的设置。用户知道并已经设置了他们希望如何阅读他们的内容(相当于用户样式表)。我们不仅不知道这一点,我们当然没有资格做出比他们更好的决定。用户是对的,我们不是;我们不应该乱来。
  • 即使我们是这些技术的专家,我们所做的改变也是针对内容的。管理和维护这是一个不现实和令人生畏的前景。

我们最好在一个样式表中处理尽可能多的内容,除非在非常特殊和不可能的情况下。

images 提示: WebAIM 对屏幕阅读器用户的偏好进行了一项调查,这在决定使用哪些默认设置进行测试时非常有价值。可以在[webaim.org/projects/screenreadersurvey/](http://webaim.org/projects/screenreadersurvey/)看。

用 CSS 隐藏内容

通常我们会使用 CSS 属性,如displayvisibility,text-indent,将 HTML 元素从页面的视觉表现中隐藏起来。例如,如果我们用一个图像替换一个标题,我们可以使用类似下面的代码:

h2 {    background: url(img/heading.png) no-repeat;    text-indent: -9999px; }

通过这样做,原始文本被移动到远离页面中心的位置,以至于现在看不见了,取而代之的是我们看到的预期背景,它通常包含以自定义的非 web 安全字体设置的文本。 9

另一种常见的情况是标记,如下所示:

<a href="page.html">Read more</a>.

用于访问链接列表的屏幕阅读器将简单地宣布“阅读更多”而没有上下文,通常在一个页面上多次。这对用户来说是非常令人沮丧的。10

为我们的发布者提供一个类可以很好地解决这个问题。下面是标记的另一个例子:

<a href="page.html">Read more <span class="accessible">about the exploding lemming epidemic</span></a>.

这是我们可以使用的 CSS:

.accessible {    position: absolute;    text-indent: -9999px; }

现在,虽然页面看起来和以前一模一样,但屏幕阅读器会读出“阅读更多关于爆发的旅鼠流行病的信息”,这与上下文无关。 11

尽管display:nonevisibility:hidden看起来像是有效的解决方案——而且对于一些屏幕阅读器来说,只要文本出现在标记中,他们就会阅读它——但这并不适用于所有的屏幕阅读器。我们建议的text-indent是目前我们对这个问题的最佳解决方案。在禁用 CSS 的情况下访问页面的用户可能会看到略显冗长的文本,但这是一个可以接受的折衷方案。


一些 CSS 作者会使用-10000em 这样的值。我们推荐-9999px,因为屏幕空间不可能比这个更宽;ems 会招致浏览器的计算,从而导致(无可否认是微小的)性能损失,并且它会节省一个字符。

再次,被浪费的链接搜索引擎会喜欢。

虽然写“点击此处阅读更多…”很有诱惑力,但你应该避免使用这种语言,因为它是鼠标专用的,用户可能使用键盘、触摸界面或其他输入设备。

仅使用键盘的用户

一些用户可能不具备使用鼠标的运动技能,或者可能就是没有机会使用鼠标。对于这些用户来说,为链接提供一个焦点状态是至关重要的,因为如果没有视觉指示,只使用键盘的用户很容易忘记选择了哪个链接。出于美观的原因,隐藏链接的轮廓可能很诱人,但是如果你这样做,请确保你提供了某种视觉替代。不要依赖悬停来访问内容或任何重要的东西,因为你不能保证用户将有一个悬停状态(例如,当使用触摸屏设备时)。

其他辅助器具

用户可以使用许多其他设备来提高互联网的可访问性:

  • 滚珠鼠标
  • 自适应键盘
  • 粘滞键
  • 眼球追踪
  • 语音导航

同样,这些设备寻求自己解决问题。如果你遵循本章概述的最佳实践,你将尽最大努力摆脱他们的方式,让他们继续做下去。

设计和布局

许多关于创建可访问网站的 CSS 建议是在信息架构、规划、内容创建和设计阶段决定的副产品。调色板和布局通常不是开发人员的责任,而是设计师或设计团队的责任。

这意味着参与创建和维护网站的每个人都应该接受某种程度的无障碍培训。尽管在理想情况下,所有相关方之间应该有一个持续的通信流,但是开发人员最终将实现已经签署的任何设计,或者基于组织的其他部分创建的指导原则来构建页面。

作为一名了解可访问性最佳实践和 web 标准的开发人员,您应该始终努力指出用户体验、内容或设计中可以改进的地方,目标是让尽可能多的人可以访问内容。

颜色

易访问性始于你所构建的基础(标记你的内容的 HTML ),但也进一步发展到你在网站上使用的颜色,甚至它的行为。

色觉障碍不一定意味着用户视力不佳或色盲(仅举几个例子),可能只是因为用户访问你的网站时使用的屏幕没有设计者设计的屏幕校准好或昂贵,你用来表达简单文本和链接之间的区别的几乎不明显的灰色阴影可能变得难以察觉——对用户来说,你的链接是看不见的。

作为一个基本准则,你应该始终确保背景和前景之间有足够的对比度。还要记住,有些用户更喜欢低对比度的配色方案(以及其他对比度非常高的配色方案,如黑色上的石灰或黄色文本)。虽然你不可能提供所有的选择,但是你应该让你的用户尽可能容易地将你的网站颜色设置成他们最舒服的颜色。

如前所述,这是一个始于设计师之手的过程,但也可以在实现阶段进行检查。

除了通过颜色区分链接(有足够的对比度)之外,通过形状将链接与文本的其余部分区分开来(例如,将链接加粗或加下划线,这是公认的链接样式)可以帮助色盲用户轻松地将链接与文本的其余部分区分开来。更进一步,你可以给链接添加底部边框,而不是默认的文本下划线,这样会使文本更难阅读,因为它会跨越下行线(像 jg 这样的字母都有下行线)。您还需要一点填充,以确保文本和下划线之间存在间隙。为此,您可以使用以下 CSS:

a {  text-decoration: none;  padding-bottom: 2px;  border-bottom: 1px solid #333333; }

在前面的例子中,我们只包含了通用的a元素选择器,但是你应该确保你有不同的风格用于访问、悬停、活动和聚焦的链接。 12

完整的默认链接样式可能如下所示:

a, a:link {  color: #333333;  text-decoration: none;  padding-bottom: 2px;  border-bottom: 1px solid #333333; } a:visited {  color: #6e5252; border-bottom-color: #6e5252; } a:hover, a:active, a:focus {  color: #e89121; border-bottom-color: #e89121; }

正如我们已经强调的,确保你的链接有一个被聚焦的视觉指示是至关重要的,因为键盘用户很容易忘记他们在页面中的位置。

这些选择器的顺序是否正确也是非常重要的。我们来看看为什么。这些是伪类选择器;:link(未访问)和:visited伪类互斥,而:hover:active:focus不互斥(可以相互配合使用,甚至可以与:link:visited配合使用)。前面代码片段中的所有选择器(除了a选择器,它更少)都具有 0,0,1,1 的特异性(一个元素选择器和一个伪类选择器——你可以阅读第三章了解更多关于特异性的细节),这意味着任何一个选择器都可以通过在代码中排在最后来覆盖所有其他选择器。知道了这一点,我们在其他状态之前定义了:link:visited状态——如果它们是最后定义的,我们将永远看不到其他状态,因为它们将被未访问或已访问的状态覆盖。


从版本 8 开始,Internet Explorer 仅支持 focus 伪类。

网上有几个工具可以帮助你通过有视觉障碍的人的眼睛来看你的网站:

  • 色彩对比检查:原创工具之一,来自 Jonathan Snook ( [snook.ca/technical/colour_contrast/colour.html](http://snook.ca/technical/colour_contrast/colour.html))。它可能不是最漂亮的,但它非常适合测试基本的配色方案和对比度。
  • 配色方案设计师:这个工具可以让你根据色盲的不同程度来验证调色板([colorschemedesigner.com/](http://colorschemedesigner.com/))。您可以轻松地将结果导出为各种有用的格式,如 HTML 和 CSS 或 XML。
  • 色盲先知:这款色盲模拟器可以在 Windows、Linux 和 Mac 上工作;它的工作原理是对屏幕应用滤镜([colororacle.cartography.ch/index.html](http://colororacle.cartography.ch/index.html))。
  • Sim Daltonist 是一个 Mac 专用色盲模拟器([michelf.com/projects/sim-daltonism/](http://michelf.com/projects/sim-daltonism/))。它在一个单独的窗口中工作,显示由几种不同的色盲类型过滤的屏幕区域。您可以用不同的过滤器同时打开多个窗口。
  • Contrast-A :这个 Flash 工具根据 W3C 的亮度、颜色和亮度指南([www.dasplankton.de/ContrastA/](http://www.dasplankton.de/ContrastA/))检查背景和前景颜色的特定组合。
  • GrayBit : GrayBit 将整页转换成它的灰度版本([graybit.com/](http://graybit.com/))。
  • 检查我的颜色:这个工具根据 W3C 的指导方针([www.checkmycolours.com/](http://www.checkmycolours.com/))检查网站元素的前景和背景颜色组合的亮度对比度、亮度差和色差。它还允许您直接在网站上选择通过验证的新颜色。

但是,当你测试网站的颜色对比时,看到一长串错误时,不要惊慌。这并不意味着你所有的内容都是不可访问的;你可以选择留下没有足够对比度的区域,但也不要保留内容。

当比较颜色对比度时,指定的阈值可能对您的设计人员来说过于严格和不切实际。相反,目标是至少 125 亮度差和至少 400 色差。

虽然这与用真实用户测试网站的可访问性并体验他的困难不同,但使用这些工具检查颜色对比不佳的情况会让你离迎合尽可能多的受众更近一步。想象一下一个色盲用户在面对一个对比度差的导航时会有多沮丧!如果他不能阅读文本,他将如何在你的网站上找到任何东西?

BBC 的无障碍网站(见图 6-4 )使用一个叫做 Barlesque 的系统来处理用户的颜色问题。

images

图 6-4。英国广播公司的无障碍网站

该页面包括一个小面板,允许用户更改字体大小(遗憾的是,只有两个步骤可用,并且都高于默认设置,但这可以在浏览器首选项中轻松设置)和配色方案。除了默认配色方案之外,还有全蓝色配色方案(针对色盲用户)、高可见度配色方案(针对视力不好的用户)和柔和配色方案(针对眼睛疲劳的用户)的选项。这些设置通过动态样式表(详见第九章)和自定义图像路径保存并应用于网站。虽然这些设置曾经适用于 BBC 的许多使用“BBC Barlesque”模板的网站,但这种方法似乎已经失宠了。这是一个很大的遗憾,因为它提供了一个处理这些配色方案的最佳方式的奇妙演示,尽管管理它们是困难的。

用户样式表和替代样式表(在本章后面会讲到)仍然可以达到同样的效果。

字体和单位

现代浏览器具有内置的页面缩放功能,可以放大整个页面,包括字体、图像、布局元素等等。但这并不是让我们不必处理老浏览器中永恒的字体缩放问题的灵丹妙药,尤其是在处理高流量网站时,使用 Internet Explorer 6 的 5%的访问者可能意味着超过 50,000 名访问者和潜在客户。缩放页面也有负面影响,比如水平滚动和内容不清晰(尽管目的正好相反)。

还有一种替代方法(很多人都在使用):只缩放文本。我们来看一个对比。图 6-5 显示了雅虎上的一篇新闻文章。,使用默认的整页缩放将它放大了一步:

images

图 6-5。一个雅虎!新闻文章,放大一步

图 6-6 显示了同一篇文章,在仅启用缩放文本的情况下放大了一步:

images

图 6-6。一个雅虎!新闻文章放大一步,仅启用缩放文本

如您所见,第一种方法立即引入了水平滚动,并开始在屏幕右侧裁剪内容。第二种方法将文本缩放了相同的级别,但保持了布局,同时也避免了图像的像素化。如果你的用户更喜欢使用默认方法,这没有问题:他们已经习惯了,你的网站很可能会像其他人的一样。但是,如果他们选择只缩放文本(或者有一个只支持这种方法的浏览器),你应该迎合他们。你可以通过努力使用相对字体大小来做到这一点。

因为 em 是一个依赖于字体大小的单位,使用缩放文本仅更改页面上的字体大小将影响使用 ems 调整大小的所有元素,而不仅仅是文本。在为 IE 6 开发时,在 ems 中调整所有内容的大小可能会很有诱惑力,这样 IE 6 就会表现出与其他浏览器的默认缩放行为相同的缩放行为(缩放整个页面)。然而,这种选择会阻止用户仅缩放文本,因此这是一种不可实现的技术。

对于固定大小的元素,如图像,像素是最明智的选择。旧的浏览器将不能缩放它们,新的浏览器将与页面的其他部分成比例缩放它们。无论哪种方式,图像都会像素化,但文本通常是内容中最重要的部分。

对于取决于视口或其容器大小的液体宽度或尺寸,百分比是最适合使用的单位。当您调整窗口大小时,或者通过其他方式修改窗口或父元素的尺寸时,这些大小可以动态变化。

患有学习障碍(如诵读困难)的用户可能会发现阅读特定字体的文本集更容易。没有一个规则可以遵循,以满足每种情况,因为一些人更喜欢阅读无衬线字体,其他人发现阅读衬线字体更容易,一些人甚至更喜欢特定的字体,如 Comic Sans。不过,我们不建议你在网站上使用漫画字体,甚至不建议你限制自己只使用某些已被证明是许多诵读困难者喜欢的字体。这里要记住的重要一点是,你的网站构建得越灵活,尽可能避免图片替换和尝试使用纯文本(即使这有时意味着在某些层面上牺牲设计)将使用户更容易以他们想要的方式浏览你的网站。

images 提示:记住,当用户在他们的电脑上、在他们的浏览器上、在他们的时间里访问你的网站时,你的网站不再是为你的,而是为他们的。它运行在他们的硬件上,他们可以用它做他们想做的事情。

你的网站应该通过文本增长测试。增加或减少一个或两个尺寸(或步长)的文本大小对您的布局来说应该不是问题。

网络字体和阅读障碍

使用 font-face 服务的一个常见问题(在第五章中有更详细的介绍)是页面在一些浏览器中从页面还没有加载嵌入字体到完全加载时会出现闪烁的非样式文本(FOUT)。对于阅读障碍者来说,这种行为会使页面无法阅读,因为他们喜欢布局的一致性,其中元素在网站的所有页面上都保持相同的位置;突然的变化(比如一个闪烁的悬停行为)会让用户很难理解和理解内容。

尝试最小化这个问题的一个简单方法是遵循第五章中提到的建议:确保在下载首选字体之前,浏览器将呈现的备用字体在纵横比上尽可能接近嵌入字体。这将使跳跃不那么显眼或者(理想情况下)不容易被注意到。

您也可以使用替代样式表(在本章后面的“样式转换器”部分中讨论)来禁用不和谐的动画或交互行为。

用户定制

用户样式表(在第三章中提到)可以在帮助残疾用户调整网站以适应他们的需求方面发挥重要作用。确保你的(作者)样式表容易被覆盖(主要是通过没有复杂和过于具体的选择器,并避免使用!important关键字),这将使这些用户的生活容易得多。

为了让用户更容易地定制您的站点,您可以做的另一件事是以 body 类或 id 的形式提供钩子,用户可以在他们的样式表上使用它们。例如,如果您向页面的body元素添加两个类(一个是站点范围的,另一个是特定于页面的),用户创建只影响您的站点或他们有特殊问题(或经常访问)的特定页面(或部分)的规则会容易得多。

让一个用户为你的网站创建一个用户样式表并不意味着你不能确保你的网站是可访问的。不可能考虑到所有可能的情况。只要确保有足够灵活的 CSS,以便不使这项任务对用户来说更加困难。

images 提示:如果一个用户不厌其烦地为你的网站创建或安装一个用户样式表,这意味着他们是一个回头客和忠诚的访问者和/或客户,他们为使用你的网站付出了相当大的努力。你想让他们的体验尽可能顺畅无阻。

风格转换者

替代样式表可以用简单的link标签来放置。存在三种不同类型的样式表:持久、首选和替代。持久样式表简单地使用带有“stylesheet”的rel属性的link标签,没有标题,就像这样:

<link rel="stylesheet" href="persistent.css" />

如果用户选择一个替代样式表,持久样式表将不会被替换。首选样式表看起来一样,但带有一个title标签:

<link rel="stylesheet" href="preferred.css" title="Basic" />

您可以通过给几个首选样式表赋予相同的标题来将它们组合在一起:

`

`

备选样式表将完全替换首选样式表。它们以与首选样式表完全相同的方式被包含,但是具有“alternate stylesheet”的rel属性。同样,您可以通过复制标题对多个文件进行分组:

`

`

然后,用户可以在浏览器中切换样式表(参见图 6-7 和图 6-8)。

images

图 6-7。火狐浏览器中的风格切换器

images

图 6-8。歌剧中的风格转换者

不幸的是,这种完美的技术有两个主要的注意事项:

  • 尽管已经存在了十多年,浏览器对这一特性的支持仍然不足。在撰写本文时,Opera 和 Firefox 是唯一支持替代样式表的主流浏览器厂商。
  • 在这两个浏览器中,没有一个保存用户对每个站点的偏好;用户的选择不会持久。 13

要提供真正的替代样式表的功能,并将其作为网站工具的一部分,允许用户选择,例如,高可见性的配色方案,您需要编写自己的代码。这需要 JavaScript,但实现起来相当简单。

请注意,无论是否应用,这些样式表仍然会下载。因此,除了专门针对有缺陷的用户的网站,替代样式表可以帮助他们,通常更好的方法是简单地确保用户可以通过他们认为最好的任何方式轻松地覆盖您的样式。

围空

WAI-ARIA 代表网页可访问性倡议——可访问的富互联网应用程序。该规范的目标是使残疾用户更容易访问 Web 内容和应用程序。根据 W3C 网站([www.w3.org/WAI/intro/aria](http://www.w3.org/WAI/intro/aria)),“它特别有助于动态内容和高级用户界面控制”,否则这些用户将无法使用。例如,对于简单的 HTML,当前没有一致的、可靠的方式来警告使用屏幕阅读器的用户页面的某个区域已经被刷新并且其内容已经被更新。

使用 ARIA,您还可以将页面区域标记为部分,以便用户可以在部分之间切换,而不是通过每个不同的链接。

ARIA 增加了对以下内容的支持:

  • 描述小部件类型(例如,“combobox”)或页面结构(例如,“banner”)的角色
  • 描述状态(例如,“checked”)或可能遭受更新的活动区域的属性
  • 小部件和事件的键盘导航

目前 ARIA 更广泛实现的一个大障碍是缺乏特性检测:没有办法检测浏览器或辅助技术中的 ARIA 支持。

由于 WAI-ARIA 是一种 HTML 技术,我们在这里就不赘述了。在撰写本报告时,WAI-ARIA 规范正处于候选人推荐阶段,可在[www.w3.org/TR/wai-aria/](http://www.w3.org/TR/wai-aria/) 14 找到。

设备和环境损害

一种经常被忽视的损伤是设备的损伤。用户可能有一台慢速计算机、没有鼠标、慢速互联网连接、阻止某些类型内容的防火墙等等。一个比开发人员想象的更常见的场景是当用户打开 CSS 但关闭图像浏览我们的网站。发生这种情况的主要原因是互联网连接速度慢。这可能是一个暂时的问题,例如在外部技术人员或内部 It 团队解决问题之前连接出现故障,或者用户可能正在从移动设备访问您的网站——这是发展中国家的常见情况,在这些国家,互联网访问往往很慢,接收数据的成本很高。不管这种情况是短暂的还是永久的,它都会发生,访问没有为此做好准备的网站,即使是在基础水平上,也是一种令人沮丧的经历。


Firefox 的扩展增加了这一功能。

你可以通过属性选择器将 WAI-ARIA 中的属性用于语义样式挂钩。但是请记住,这是一个较慢的选择器,并不是所有的浏览器都支持。

渐进增强(下一节将详细介绍)可以解决大多数问题,但不是万灵药。如果你已经建立了一个只有标记看起来不错的网站,那么你已经成功了。如果您随后只在启用 CSS 的情况下进行测试,这将暴露您可能会忽略的问题,比如背景图像下缺少背景颜色或背景颜色不合适。 十五

在第八章中,我们将演示如何拦截请求并修改它们,例如模拟慢速连接。尽可能多的测试所有的东西,并以渐进的增强为目标,而不是优雅的退化。那些是什么?你问这个真有趣…

渐进增强还是优雅退化?

渐进增强和优雅降级是看待同一个问题的两种方式:如何处理不支持我们已经使用的功能的浏览器。

渐进增强,你从最小公分母开始;你首先要让网站为每个人服务。这意味着良好的语义、合理的内容排序、没有 CSS、没有图像、没有 JavaScript 等等。您的标记本身应该工作良好。然后添加这些层,这样,如果由于某种原因其中一层出现故障或不受支持,站点内容仍然可以以合理的方式使用。显然,这是一种可行的方法——我们确保尽可能满足最广泛的受众,不遗漏任何人。

优雅的退化与此相反。我们同时构建所有的层,为大多数人,但不是所有人,带来了很好的用户体验。然后,我们确保当它失败时,它以合理的方式失败,并且我们仍然可以访问我们的内容。这可能意味着构建页面的替代版本,或者就如何解决用户可能遇到的任何问题提出警告或指导。优雅降级的一些很好的例子可能是图像的alt属性或者没有启用 JavaScript 的浏览器的noscript标签。优雅的降级总比什么都不做好,但是实现降级意味着(在我们看来)你的优先级错了。通常,较低层次的潜在失败被忽视、忽略或遗忘;事后实现的成本更高。这也意味着我们已经定义了我们的最高水平——我们从那里开始,一路向下,当新技术被广泛接受时,我们几乎没有空间去接受它们。

另一方面,渐进式改进使我们不可能将设备受损的用户抛在后面。一切都是建立在底层之上的,所以它们是实现我们希望大多数用户会使用的全功能网站的关键。因为我们的顶层是建立在一个坚实的基础上的,所以很容易在以后的日子里加入更新、更聪明的东西,并对我们没有排斥任何现有的用户群感到安全。

渐进增强最好的例子就是 CSS 本身。它位于我们的标记之上,并对其进行了改进。我们可以进一步增强 CSS3 和实验功能,使用媒体查询来确保我们不会破坏其他人的体验。然后,我们可以在其上放置 JavaScript,以此类推。

优雅的退化似乎是最快的发展选择,而且是在短期内。我们可以快速构建最高水平的体验,并从那里开始工作。然而,增加较低的水平消耗了时间。渐进式增强需要更多的前期考虑,但会带来更健壮的技术解决方案。然而,也有例外。我们知道预算不是无限的,试图为每个人服务并不总是符合您组织的财务利益。在这种情况下,我们建议您尽最大努力利用现有资源,使用您的报告工具针对您最常见的用户统计数据。此外,临时性的网站(如短期促销网站或季节性活动网站)不太可能在线很长时间后就会被永远丢弃。在这种情况下,渐进式改进的投资回报率(ROI)可能会很低,我们不建议您在将被丢弃的东西上投入太多时间。在这种情况下,优雅地降级(或者简单地优雅地失败)可能是一个更可行的选择。


这样做的一个附带好处是,当图像加载时,用户会感觉不那么不舒服。

雅虎!分级浏览器支持(GBS)方法(在本章的下一节中有更详细的解释)更倾向于渐进增强而不是适度降级,因为它“将内容置于中心,并允许大多数浏览器接收更多内容(并向用户显示更多内容)”,而后者“优先显示,并允许不太广泛使用的浏览器接收更少内容(并向用户提供更少内容)”([developer.yahoo.com/yui/articles/gbs/](http://developer.yahoo.com/yui/articles/gbs/))。

【CSS3 会有害吗?

CSS3 和 HTML5 一样,是网络行业最新的流行词汇。这意味着许多开发人员没有考虑到他们行为的潜在后果就跳了进去。使用 CSS3 会导致网站无法访问,这一点非常明显:

  • 辅助设备可能看不到::before::after等伪元素。用它们做装饰,不要做重要内容。 16
  • 与纯色相比,渐变会使颜色可见性问题更加复杂。
  • 对关键功能使用过渡、变换和动画可能会使不支持这些功能的浏览器的用户无法使用这些功能。

处理这些问题的最好方法是理解我们在本章中解释的潜在问题。请记住,尽可能逐步增强,并为这些功能可能不受支持的情况提供后备。注意一些新技术可能带来的颜色可访问性问题,并尝试提供替代样式表。将过渡、变换和动画用作装饰效果,而不是重要功能。

这主要是常识。记住要在老版本的浏览器中测试,并预先解决用户将会遇到的问题。渐进增强是你的朋友。

分级浏览器支持(GBS)

2006 年,雅虎!发表了由(当时的)高级前端工程师 Nate Koechley 开发的 GBS 方法。GBS 将浏览器分为三个不同的支持级别:A 级、C 级和 X 级。所谓的支持并不意味着某些浏览器将被锁定在内容之外,而是提供不同的体验,同时仍然能够完全访问它。让我们看看每一个不同的等级实际上意味着什么:

  • A 级是最高支持级别。该组中的浏览器将获得完整的视觉和功能体验,并被列入白名单。所有的 A 级浏览器都应该被测试,任何错误都被标记为高优先级。该列表中的浏览器包括(除其他外):Safari 5;Chrome 最新稳定版;火狐 3.6;而 IE 6 到 8。
  • C 级浏览器只呈现 HTML。这些都是功能较弱的老浏览器(只代表很小一部分用户)。应该忽略样式和行为,只测试一小部分浏览器样本(或者使用禁用 CSS 和 JavaScript 的现代浏览器),但是 bug 也应该得到高优先级。此支持级别的网站被列入黑名单。此列表(在撰写本文时处于草案中)包括低于 6 的 Internet Explorer 版本(包括 Mac OS 版本);低于 3 的 Safari 和 Firefox 版本;低于 9.5 的 Opera 版本;低于 8 的 Netscape 版本。预计 2011 年第一季度,IE 6 将下调至 C 级支持级别。
  • X 级包括边缘浏览器。这些是罕见的或模糊的浏览器,不再被开发的浏览器,或全新的(未经测试的)浏览器;它们将和 A 级浏览器一样强大。然而,与 A 级浏览器不同,X 级浏览器没有经过测试,也没有针对它们的错误记录。

16 严格来说,这些伪元素在 CSS2.1 中是作为:before:after引入的。

所有浏览器都属于三个支持级别之一,因此不存在“不支持”的浏览器,因为内容对所有人都可用。雅虎!must 佩奇说“在现代网络开发中,我们必须支持所有的浏览器。选择排除一部分用户是不合适的,并且,对于‘分级浏览器支持’策略来说,是不必要的”([developer.yahoo.com/yui/articles/gbs/](http://developer.yahoo.com/yui/articles/gbs/))。它还说(我们也同意)“期望两个使用不同浏览器软件的用户有相同的体验是没有拥抱或承认网络的异质性本质。”

images 提示: jQuery Mobile 有自己的 GBS ( [jquerymobile.com/gbs/](http://jquerymobile.com/gbs/)),这是一个关于移动浏览器及其各自可能的功能的有用指南。

虽然采用了雅虎!GBS 逐字逐句是一个简单而合理的策略来选择你应该支持哪些浏览器,它更有意义的使用它作为一个基础,并参考您的报告工具,以确保 GBS 匹配你的实际流量。如果不符合,就修改。当在您的组织内提出 bug 时,您应该使用 GBS 图来确定问题的严重性,从而确定修复这些 bug 的重要性。使用一致的方法来提出 bug 并测量它们的严重性将导致最重要的 bug 首先被修复,并且每个测试人员以更准确和可预测的方式提出 bug。

images 提示:如前所述,SEERS ( [zeroedandnoughted.com/standardised-bug-reporting-with-seers/](http://zeroedandnoughted.com/standardised-bug-reporting-with-seers/))是 Antony 创建并倡导的一种方法,旨在为基于浏览器的 bug 测试创建一个标准。利用 GBS 是这个过程的关键。

总结

不可能考虑到你的访问者可能遇到的每一个可访问性问题,不管是暂时的还是永久的。正因为如此,你需要让你的网站具有灵活性,易于被所有人调整和阅读——这是你的网站,但它是你的访问者的浏览器,他应该拥有最终决定权。

重要的是不要将此视为团队的负担。此外,您不应该将此视为发展中的一个步骤。可访问性的考虑应该经常出现在你的团队的脑海中,并且应该始终是一个关注点。通过使你的网站更容易访问,你将确保尽可能多的人可以访问你的内容和/或购买你的产品,而不会感到沮丧,转向你的竞争对手。

这些简单的技巧也会让你的网站更加灵活和易于维护(例如,想想使用图像替换会带来的麻烦)。因此,你也将拥有搜索引擎更友好的网站。

这里要传达的最重要的一点是,每个用户、浏览器或设备都应该能够访问您的内容和服务,无论其功能或环境如何。

在下一章,我们将讨论许多可以解析和呈现 CSS 的设备。我们将研究这些编码的一些限制和性能含义,以及如何针对媒体类型和媒体查询进行编码。

七、设备

在高流量的网站上,不寻常的设备比其他设备构成了更多的访问者。根据你网站的目标人群和你的报告工具的结果,你可能需要认真考虑这些设备。当我们从 CSS 的角度讨论设备时,实际上我们只是在谈论具有不同功能的浏览器。无论我们的 CSS 是提供给手机,笔记本电脑,还是搜索引擎蜘蛛,本质上它只是另一个网络浏览器,具有不同的分辨率和不同的能力来处理我们的 CSS。

每种设备都有不同的考虑因素;使用打印机时,我们需要注意分页符和灰度的清晰度,以及避免打印不必要的内容。对于移动设备,我们需要关注不寻常的屏幕尺寸和分辨率,以及缓慢的处理器或较差的 CSS 支持。屏幕阅读器也可以被认为是设备,但是我们在前一章已经详细讨论过了。

在本章中,您将了解

  • 媒体类型
  • 媒体查询
  • 媒体特征
  • 详细打印
  • 具体的 SEO 注意事项
  • 移动设备
  • 其他设备

媒体类型

您的网站收到的流量越多,被不太可能或不寻常的设备访问的可能性就越大。虽然较小的网站可以忽略或忽视特定的设备和/或网络浏览器,但对于较大的网站来说,它们可能构成足够大的数量,需要作为重要的用户群来考虑。大公司非常重视他们的品牌和客户体验,至少你应该向任何有兴趣访问你网站的人展示合理的体验,不管他们用什么来浏览。为此,您需要实现各种方法来检测它们的平台并提供最合适的内容。

我们用来引用外部样式表的 link 标签中的 media 属性旨在标识样式表所针对的设备类型。如果设备/浏览器支持,它们将忽略不相关或未知的样式表媒体类型,并且不包括文件的内容。值得注意的是,大多数现代浏览器仍然会下载文件,即使它们不打算解析内容。 1 这意味着从性能角度来看这种方法没有用——它实际上比不使用它会招致更多的 HTTP 请求——而只对特定的浏览器或设备有用。

你实际上可以通过三种方法来定位媒体。第一个是在链接标签内,就像这样:

<link rel="stylesheet" href="style.css" media="**screen**" />

第二种是通过@import 指令:

@import url(style.css) **screen**;

@import 命令必须始终位于样式表中任何其他规则之前(其他@import 命令除外)。同样,大多数现代浏览器下载文件,即使它们不会解析文件的内容。无论如何,我们不建议您使用@import 规则,因为它们会阻止对文件其余部分的解析。

最后一种方法是@media 指令,它允许我们只使用特定媒体类型的文件的特定部分。这里有一个例子:

@media **screen** {    body {       font-family: arial;    } }

这只针对报告支持屏幕媒体类型的浏览器和设备。

对于所有这三种方法,您可以用逗号分隔媒体值,以针对多种类型,如下所示:

`

@import url(style.css) screen, print;

@media screen, print {
   body {
      font-family: arial;
   }
}`

在这些情况下,如果浏览器或设备仅支持其中一种媒体类型,仍将解析文件/规则。

对于每种媒体类型,都有一些重要的因素需要考虑。这些被称为媒体组

分页媒体(由页面组成的媒体,如打印的文档,而不是像屏幕上显示的网页一样的长文档)支持一些连续媒体没有的额外属性 2 网格媒体(与位图媒体相反)认为固定宽度字符很重要。例如,盲文媒体需要字符具有可预测且一致的宽度,以便用户能够理解。我们可以按静态、交互或两者来分组,表明目标是只读的还是可以交互的。媒体还可以分为视觉、听觉、语音(对于屏幕阅读器)或触觉(对于通过触摸进行交流的设备;例如盲文)。


这是为了使它们的内容仍然可以通过 CSS 对象模型(CSSOM)来访问,并被列在 document.styleSheets 中,可以通过 JavaScript 来访问 document . style sheets,以给出附加到文档的所有样式表的详细信息。

我们将在本章后面提到它们。

让我们来看看 CSS2 规范中定义的媒体类型。

全部

虽然不是真正的媒体类型,但 all 适用于以下所有媒体类型,因此适用于所有媒体组。如果不指定媒体类型,这是默认行为。

盲文

盲文是一种传达字符的触觉方法,主要由盲人使用。盲文触觉反馈设备的功能各不相同。该规范将这种媒体类型描述为连续的而不是分页的(可以输出分页媒体的盲文打印机被认为是在浮雕媒体类型中)、触觉的、网格的以及交互和静态的。

浮雕

浮雕介质类型类似于盲文类型,但旨在通过盲文打印机输出。因此,它被认为是分页的(而不是连续的)和静态的。除此之外,它与盲文类型完全相同。

手持

手持型旨在瞄准手机等手持设备。这些设备通常连接速度较慢,屏幕较小,因此需要区别对待。由于手持设备的功能差异很大,它们被认为是除了触觉以外的所有媒体的一部分。??

手持设备的性能从差到强,以及介于两者之间,并提供许多不同的浏览器。由于它们变化如此之快,我们建议使用媒体查询(将在本章后面介绍)来定位它们,而不是仅仅依赖媒体类型。但是请记住,您不能依靠媒体类型或媒体查询来控制下载的内容;浏览器或设备可能会选择下载所有内容,因此没有性能优势。

由于目前的手持设备,如智能手机和平板电脑,尽最大努力呈现网络,而不是网络的精简版本,因此大多数设备实际上都是“屏幕”媒体类型的目标。WebKit (Mobile Safari 和 Android)和 Firefox Mobile 完全忽略这种媒体类型。如果用户在设置中切换到“手持模式”,或者文档具有已知的移动 doctype,Opera Mini 和 Opera Mobile 将尊重它。

打印

打印介质类型专门适用于打印介质输出,因此被视为分页、可视、网格和静态。我们将在本章后面详细讨论印刷媒体和分页媒体。


事实上,自 2009 年 9 月以来,诺基亚已经开始在手机上试验盲文阅读器。

o 投影

投影媒体类型用于投影输出,通常是投影仪。因为这种输出通常来自无人终端,所以被认为是分页的、可视的和位图的。规范将它列为仅交互,我们认为这是一个错误。Opera 在全屏呈现模式(Opera Show)下尊重这种媒体类型。我们不知道任何其他浏览器或设备实现了这一点。

屏幕

画面媒体类型无疑是最常见的类型。主要用于电脑屏幕(台式机和笔记本电脑),这是你最有可能使用的媒体类型,与一起使用。几乎所有的设备都使用这种媒体类型,不管它是否是为他们设计的,因为这些设备中的大多数都打算在 web 浏览器中呈现互联网,而不是一个简化的版本。屏幕被认为是连续的、位图的,既有交互的也有静态的,既有视觉的也有听觉的。

演讲

语音媒体类型适用于任何能够以声音方式阅读页面内容的设备或应用程序,例如屏幕阅读器或其他辅助技术。在旧的规范中,它被称为听觉(现在已被否决),所以最安全的做法是同时针对两种媒体类型,就像这样:

<link rel="stylesheet" href="style.css" media="**speech, aural**" />

这种媒体类型被认为是连续的、语音的、交互的和静态的。网格或位图媒体组在这种情况下不适用。你可以在[www.w3.org/TR/CSS2/aural.html](http://www.w3.org/TR/CSS2/aural.html)阅读更多关于这种特定类型的样式表。

tty

这种媒体类型(是“电传打字机”的缩写)适用于使用“固定间距字符网格”(即宽度完全相同的字符网格)的媒体。由于这种媒体类型没有字符像素大小的概念,作者不应该提供它们。目前这种类型的设备非常少,支持互联网的就更少了,所以对这种媒体类型的支持和使用非常少。这种媒体类型被认为是连续的、可视的、网格的、互动的和静态的。

电视

这种媒体类型是为电视机设计的。尽管规范将这些设备列为“低分辨率、彩色、有限滚动屏幕、可用声音”,但电视机的性能差异很大,这种描述通常是不正确的。当前的大型电视通常比它们的监视器提供更高分辨率的屏幕,有时甚至包括网络浏览软件和滚动能力。由于这些功能上的不一致,电视媒体类型被认为是连续的和分页的、可视的、音频的、位图的、交互的和静态的。

images 注:虽然屏幕和印刷品是目前最常用的媒体类型,但我们知道除了tty之外,还有使用所有媒体类型的设备。媒体类型被添加到规范中,但不一定由设备制造商实现,这通常是因为开发人员很少实现。

其他设备可能支持此列表中未定义的媒体类型,但我们建议不要使用它们。您可以在[www.w3.org/TR/CSS2/media.html](http://www.w3.org/TR/CSS2/media.html)了解更多关于媒体类型的信息。由于媒体类型现在很少足以描述设备或浏览器的功能,CSS3 引入了媒体查询,它为我们提供了必要的工具来根据设备的特性和功能而不是(或与)其媒体类型来定位设备。大多数当前的移动设备(包括平板电脑)都支持媒体查询,如果您打算使用这些设备,我们建议您使用它们。

媒体查询

媒体类型可以让我们有能力针对我们可能期望浏览我们网站的一些设备,但它们在设备中的实现是不完整的。新设备的支持更加可预测,尤其是那些高流量网站可能会有大量访客的设备。同样,这些设备可能只占你总流量的一小部分,但仍然是很大一部分人。媒体查询让我们能够更好地控制目标设备和浏览器。

媒体查询由两部分组成:一个媒体类型和零个或多个将根据媒体特征进行检查的查询。当媒体类型与设备类型匹配并且所有查询都为真时,应用媒体查询。如果列出了多个媒体查询(使用逗号分隔的列表),则只有一个媒体查询需要为真。

这里有一个简单的例子:

<link rel="stylesheet" href="style.css" media="screen and (min-width:800px) and (max-width:1500px)" />

除了针对支持“屏幕”媒体类型的浏览器和设备,我们还声明设备的视窗宽度必须至少为 800 像素,最多为 1,500 像素。我们在上面括号中命名的功能称为“媒体功能”。您可以将逗号分隔的媒体查询(与媒体类型完全一样)作为速记,如下所示:

<link rel="stylesheet" href="style.css" media="tv, projection, screen and (min-width:800px) and (max-width:1500px)" />

在这种情况下,每个逗号分隔的值都是作为一个整体进行计算的,(跟在“和”后面的部分仅适用于紧接在它们前面的媒体类型)。前面的媒体查询与下面的相同:

`

`

每个列表中可以使用多个“and”子句。您还可以使用“not”来否定整个媒体查询。但是,您不能否定查询的个别部分。以下将起作用:

<link rel="stylesheet" href="style.css" media="not screen and (min-width:800px) and (max-width:1500px)" />

这将只解析任何浏览器或设备的 style.css,如果不应用“not”关键字,查询将失败(不支持媒体类型screen,或者视口宽度小于 800 像素,或者视口宽度大于 1500 像素)。以下(以及尝试相同操作的其他变体)不起作用:

<link rel="stylesheet" href="style.css" media="screen and (min-width:800px) and not (max-width:1500px)" />

对于任何习惯于基本条件逻辑的开发人员来说,这种粒度的缺乏都是令人沮丧的。要以这种方式定位多个功能集,必须首先将默认行为应用于所有设备,然后为每个设备覆盖此行为,如下所示:

`/* Applied to every device and browser */
body {
   color: black;
}

@media screen and (min-width:800px) {
   /* Only applied to screen devices with a viewport width of at least 800 pixels */
body {
   color: red;
   }
}`

每当用户代理(浏览器或设备)不理解媒体查询时,它就被认为是“不是全部”——即,它将不应用链接的样式表或查询的内容。

“only”关键字可用于确保只有符合 CSS3 的浏览器才会应用该样式表。

<link rel="stylesheet" href="style.css" media="only screen" />

在媒体查询的开头使用“only”关键字将对不支持它们的旧浏览器隐藏媒体查询(因为它们会认为“only”是媒体类型的名称),而兼容的浏览器将忽略“only”关键字并检查其后的媒体查询。

和在link标签中一样,媒体查询可以在 CSS 文件中以另外两种方式使用,就像媒体类型可以单独使用一样:

`@import url(style.css) screen and (min-width:800px) and (max-width:1500px);

@media screen and (min-width:800px) and (max-width:1500px) {
   body {
      font-family: arial;
   }
}`

images 注意:浏览器会下载链接的文件,即使这些文件不会应用于相关文档。

当媒体特性将评估为非零值(不考虑单位)时,您不需要提供值。例如:

<link rel="stylesheet" href="style.css" media="all and (width)" />

该样式表将总是被应用,因为视口的宽度将总是大于零(可能主要是听觉设备除外)。你不会使用这种方法;我们只是把它作为一个例子。

在我们深入了解可用的媒体特性之前,有必要花点时间讨论一下视口本身。视区是设备上可用的呈现大小,不包括任何浏览器镶边。

随着 Mobile Safari 的推出,苹果还推出了viewport meta tag4

viewport meta标签给你一些对视窗大小和行为的控制。虽然这是 HTML,但理解我们将在本章中涉及的一些概念是很重要的。这里有一个简单的例子:

<meta name="viewport" content="width=device-width" />

名称“viewport”标识了这个meta标签。内容属性包含我们想要传递的值。属性是用等号分开的键/值对;您可以用逗号分隔它们,想要多少就有多少。我们可以使用两个重要的常数:

  • 设备宽度:设备的实际像素宽度(横向)
  • 设备高度:设备的实际像素高度(横向)

还有六个我们可以使用的重要属性:

  • width :视口的初始宽度(横向),以像素为单位。该值也可以是这两个常数中的任意一个。默认值(在 Mobile Safari 上)为 980 像素。
  • height :视口的初始高度(横向),以像素为单位。该值也可以是这两个常数中的任意一个。默认值是根据设备的宽度和纵横比自动设置的。
  • 初始比例:比例是一个乘数,用于计算视口的大小。例如,如果我们将视口宽度设置为 400 像素,比例设置为 1.5,则视口实际上将为 600 像素,并在可视区域之外水平延伸 200 像素。此属性设置初始比例值。默认情况下,它通常会被自动设置为适合可见区域中整个网页的值。用户可以缩放设备来调整该值。
  • 最小比例:设置用户可以缩放到的最小比例。
  • 最大比例:设置用户可以缩放到的最大比例。
  • 用户可缩放的(user-scalable):设置用户是否可以调整视窗的大小——是否可以缩放。潜在值为。默认值为是。

4 你可以在developer . apple . com/library/safari/# documentation/apple applications/Reference/safari html ref/Articles/meta tags . html阅读关于 viewport 元标签的内容,以及在developer . apple . com/library/safari/# documentation/apple applications/Reference/safari web content/usingthe view port/usingthe view port . html阅读关于 viewport 的具体内容。

images 提示: Opera 提出了用 CSS 实现同样事情的方法,而不是用 HTML,这样更合适。遗憾的是,目前还没有真正的支持,但是你可以在[dev.w3.org/csswg/css-device-adapt/](http://dev.w3.org/csswg/css-device-adapt/)阅读规范。

重要的是,视窗可能会超出可视区域,但绝不会小于可视区域。这很重要,因为我们在媒体特性中使用了viewport。CSS3 规范中定义了许多媒体功能。我们将在下面的章节中列出这些。

宽度

宽度媒体功能适用于视窗(连续媒体)或“页面框”(分页媒体)的宽度。 5 有虚拟视口的地方(即比例大于 1)这适用于整个视口的宽度,而不仅仅是可渲染的屏幕空间。

它接受大于零的数字(任何小于零的数字都会使查询无效)。您可以(像许多媒体功能一样)在 width 前面加上min-max-,就像上面的例子一样。当分别大于或等于和小于或等于时,它们实际上起相同的作用。 6

可以使用任何单位,但是在使用相对大小的单位时,这些单位将相对于文档的根元素(即html标签)。

身高

高度媒体功能的行为与其“宽度”功能完全相同,但应用于视口的高度。

设备宽度

设备宽度媒体特性描述了设备的整个呈现表面(即可显示区域)的宽度。对于屏幕,这是屏幕的宽度。对于分页媒体,这是页面的宽度。同样,您可以使用max-min-作为前缀,任何小于零的值都将导致无效的媒体查询。这使用实际的屏幕宽度,而不是您可能已经用 viewport meta标签设置的虚拟视窗,或者用户可能已经通过在他的设备上缩放页面而修改的虚拟视窗。

设备高度

除了高度之外,设备高度媒体功能与device-width完全相同。


在 www.w3.org/TR/2009/CR-CSS2-20090908/page.html#page-box的 CSS 2.1 规范中定义的 5

我们决定避免使用<和>字符,因为它们可能会与 HTML 中的字符冲突。

方位

方向媒体特征有两个可能的值:landscapeportrait。如果宽度视口大于高度,则方向为横向。否则就是人像。如果在查询中使用此媒体功能,您应该始终向其传递一个值。使用不带值的方向会导致浏览器或设备不应用 CSS。

images 注意:没有方向的“平方”值。如果宽度和高度值完全相等,则方向被视为纵向。

长宽比

纵横比媒体特征是视口的宽度与高度之比,用正斜杠分隔。这里有一个简单的例子:

@media screen and (aspect-ratio: 4/3) {    … }

设备纵横比

device-aspect-ratiomedia 特性描述了设备的整个可呈现区域的宽度与高度之比,用正斜杠分隔。

颜色

颜色媒体特性描述了设备每个颜色组件的位数。例如,如果器件的红、绿、蓝三色各使用四位,则该值为四。如果由于某种原因,每个组件使用了不同的位数,则最小的位数就是值。如果设备不是彩色的,或者不是可视的,这个数字将为零。您可以将min-max-用于彩色媒体功能。

颜色指数

颜色索引功能用于查询设备一次可以显示的颜色数量。这通常比彩色媒体功能更直观。设备通常支持 16 个;256;65,536;或者 16,777,215 种颜色。您可以将min-max-color-index一起使用,非彩色或不可视的设备会将该值显示为零。

images 注意:在媒体查询中,您可以在数值中使用逗号,这对易读性有很大帮助。下面的例子就可以了:

    <link rel="stylesheet" href="style.css" media="screen and (min-color-index:1,001)" />

单色

单色媒体特性描述了设备支持单色显示的位数(实际上,数字越大,可以显示的黑白灰度就越多)。您可以将min-max-用于此媒体功能,如果设备是彩色的或没有显示,该值将为零。

分辨率

分辨率媒体特性以每英寸点数(DPI)或每厘米点数(DPCM)来描述设备上的像素密度;您应该在媒体查询中使用这些单位(分别为 DPI 和 DPCM)。此媒体功能支持min-max-前缀。如果使用带有非方形像素和min-resolution的设备,将使用密度最小的尺寸(DPI 较小的尺寸)。使用max-resolution时,将比较最密集的尺寸。如果对非方形像素使用“分辨率”(用于精确匹配),则样式表永远不会被应用。此介质功能仅适用于基于位图的介质,将其应用于网格介质会导致整个介质查询被视为“非全部”

扫描

扫描媒体功能专门适用于电视(以及tv媒体类型)。适用值为“渐进”和“扫描”。如果设备不是电视机,则该值将评估为零。

网格

网格媒体功能描述设备是基于网格还是基于位图。对于网格输出设备,该值为 1;否则为 0。

images 提示:媒体查询将被即时评估。也就是说,如果您已将媒体查询设置为应用于视窗小于 900 像素的设备,并且您正在使用具有可调整视窗大小的设备,则调整窗口大小将导致查询立即被应用或取消应用。

您可以在[www.w3.org/TR/css3-mediaqueries/](http://www.w3.org/TR/css3-mediaqueries/)了解更多关于媒体查询的信息。

自从在 WebKit 中引入 CSS 转换、过渡和动画以来,规范中定义的基本媒体特性显然不足以只针对支持它们的浏览器。为此,WebKit 定义了四个专有媒体功能,将在以下几节中讨论:

二维变换

transform-2d media 特性描述了 CSS 变换在二维空间中的可用性。如果可用,该值将计算为 1,如果不可用,将计算为 0。

变换-3d

这个媒体特性描述了 CSS 转换在三个维度上的可用性。如果可用,该值将计算为 1,如果不可用,将计算为 0。由于支持跨二维的 CSS 转换是该特性可用的必要条件,因此您可以根据该特性的值 1 安全地推断出transform-2d的值也为 1。

过渡

过渡媒体特征描述 CSS 过渡的可用性。如果可用,该值将计算为 1,如果不可用,将计算为 0。

动画

动画媒体特征描述了 CSS 过渡的可用性。如果可用,该值将计算为 1,如果不可用,将计算为 0。

images 注意:遗憾的是,你不能用“not (-webkit-animation)”这样的话来定位不支持这些媒体功能的设备媒体查询中的任何未知特征将立即使媒体查询等同于“不是全部”。对于这些设备,您需要设置默认行为,然后覆盖它,就像我们之前演示的那样。

虽然在[webkit.org/specs/MediaQueriesExtensions.html](http://webkit.org/specs/MediaQueriesExtensions.html)[developer.apple.com/library/safari/#documentation/appleapplications/reference/SafariCSSRef/Articles/OtherStandardCSS3Features.html](http://developer.apple.com/library/safari/#documentation/appleapplications/reference/SafariCSSRef/Articles/OtherStandardCSS3Features.html)描述并记录了所有这四种媒体特征,但事实上它们(在撰写本文时)无法在实践中使用。它们都需要特定于供应商的 WebKit 前缀才能正常工作。以下是工作介质功能的完整列表:

  • -webkit-transform-2d
  • -webkit-transform-3d
  • -webkit-transition
  • -webkit-animation

使用媒体查询可能会令人沮丧,但重要的是要记住规范一直在发展。现在,这里有四个要点可以帮助您在开发和使用媒体查询时避免困惑:

  • “not”关键字否定整个查询。您不能否定媒体查询的单个元素。
  • 在介质查询中没有“或”运算符(尽管您可以用逗号分隔整个介质查询)。
  • 无论媒体查询如何,都将下载外部引用的文件。不要假设将媒体类型应用于单独的文件会给你带来任何 HTTP 性能上的好处。相反,这实际上会导致更多的文件请求。
  • 特定于 WebKit 的媒体功能需要供应商前缀。

现代化

由开发人员 Faruk Ateş、Paul Irish 和 Alex Sexton 创建的 Modernizr ( [www.modernizr.com/](http://www.modernizr.com/))是一个 JavaScript 库,用于检测和公开浏览器功能。它的工作原理是向页面的html元素添加类;这些类指示浏览器中是否存在特定的特性(准确地说是 HTML5 和 CSS3 特性)。Modernizr 不支持浏览器中的功能;它只是说明它是否可用。它还支持在 Internet Explorer 等功能较弱的浏览器中设计新的 HTML5 元素。

要使用 Modernizr,您只需要在 HTML 文档的头部链接到它,就像这样:

<script src="js/modernizr-1.6.min.js"></script>

其次,您需要在您的html标签中添加一个“no-js”类。这将是页面的默认状态。如果 JavaScript 关闭,Modernizr 将无法工作,您将能够检测到这一点,并向您的用户提供页面的非 JavaScript 版本:

<html class="no-js">

如果启用了 JavaScript,类似于下面的各种类将替换该类:

<html class="js canvas canvastext geolocation rgba hsla multiplebgs borderimage borderradius boxshadow opacity no-cssanimations csscolumns no-cssgradients no-cssreflections csstransforms no-csstransforms3d no-csstransitions video audio cufon-active fontface cufon-ready">

在前面的示例中,我们可以看到有问题的浏览器(在本例中为 Firefox 3.6)不支持 CSS 动画、CSS 渐变、CSS 反射、3D 中的 CSS 变换或 CSS 过渡(分别来自类“no-cssanimations”、“no-cssgradients”、“no-cssreflections”、“no-csstransform3d”和“no-csstransitions”)。它支持其他功能,如画布和地理定位。

有了这些知识,我们现在可以相应地编写 CSS。假设我们想给页面的body元素添加多个背景。我们可以首先为所有浏览器指定默认样式,然后用一个更具体的样式覆盖该样式,该样式只针对支持该功能的浏览器,如下所示:

body {         background: url(simple.png) top left repeat-x;
} .multiplebgs body {         background: url(multiple-top.png) top left repeat-x,         url(multiple-bottom.png) bottom left repeat-x; }

在前面显示的html元素的例子中,Firefox 3.6 可以理解并显示多种背景。例如,Internet Explorer 8 将只显示“simple.png”背景图像,因为它使用的类将是“no-multiplebgs”。

同样重要的是要注意,在前面的例子中,Firefox 3.6 实际上会下载这两个图像,因为这两个规则都可以应用(即使不太具体的规则被覆盖),所以即使我们确保满足用户可以和不可以看到这些功能,我们也通过让他们下载完全不必要的文件来惩罚那些可以看到这些功能的用户。我们可以通过使用"来避免这种情况。在我们的第一条规则中,不要只说“body”

尽管这是一个简单而有用的解决方案,但在您考虑使用它之前,请记住,Modernizr 会产生非常冗长的 CSS,所有用户都可以下载它,但不一定会应用它,而且它会在您的 CSS 中添加一个 JavaScript 依赖项,这远非理想。

打印样式表

打印样式表需要被认为是一个特例。用户经常想要打印你网站上的页面,你可以用不同于一般用户的方式来满足他们的需求。打印你的内容的用户是有兴趣的用户,用不必要的内容或不适合他们媒体的页面来打击他们会降低他们对你的网站和你的公司的好感。另一方面,以清晰友好的形式向他们提供他们需要的信息表明了对细节的关注和对他们需求的考虑。制作一个基本的打印样式表是非常容易的,如果你的标记和 CSS 是经过深思熟虑的,它通常会非常简洁,并且在你的整个网站上运行良好。

包含打印样式表很容易。我们将在此重申如何最好地实现这一目标:

<link rel="stylesheet" href="print.css" media="print" />

您可以使用@import 命令来实现同样的事情,但是这对于非常旧的浏览器来说是行不通的。您还可以使用@media 命令专门针对打印设备,但这同样会排除较旧的浏览器。对于打印您的内容的用户,您需要考虑在这个样式表中包含几件事情。

  • 用户想要的是页面中的内容,而不是导航或其他多余的元素(如表单和其他在静态媒体中无用的交互元素,以及广告,除非你特别约定在打印时显示它们,这是不太可能的)。在这些元素上考虑一个类似“noprint”的类,这样就很容易用display:none;隐藏它们。或者,您可以使用一个类,比如“print ”,只用于那些您希望输出到打印机的元素。
  • 考虑一下您是想要扩展您的屏幕样式表(通过使用媒体类型all作为该样式表)来维护现有品牌,还是完全替换它(通过使用媒体类型screen作为该样式表)。完全替换它通常会产生良好的结果,并避免不可预测的陷阱,这在您有大量页面需要考虑时是很常见的。
  • 衬线字体在印刷媒体上更容易阅读(屏幕媒体上呈现的信息则相反)。考虑另一种衬线字体堆栈。
  • 避免在打印样式表中更改字体大小。浏览器默认值通常是合适的。
  • 如果你改变字体大小,以磅为单位(pt)。12 分一般没问题。
  • 将任何容器调整到页面的宽度(100%)并移除它们的边距。这将确保它们填充被移除元素的空间以及可打印区域。这也有助于确保它们不会溢出到第二个水平页面上,从而导致糟糕的打印体验。您还应该使用@page 规则在打印的页面上添加一个小的页边距,这一点我们将在本节稍后讨论。这将有助于舒适的阅读,尤其是在能够进行无边框打印的打印机上。
  • 不要让你的容器漂浮。这可能会产生不可预知的结果。
  • 默认情况下,打印时通常禁用背景图像和颜色,因此请确保您指定的深色(或黑色)字体颜色在白色背景下清晰可见。
  • 如果打印背景图像或颜色,请将背景设置为白色。这可以避免模糊或不清楚的内容,可以节省墨水,并使页面打印速度更快。
  • 出于与前面相同的原因,移除不需要背景的元素上的背景(将其设置为透明)。
  • 链接在印刷媒体上不明显,也没有悬停状态。给你的链接加下划线,这样可以清楚地表明它们是链接。我们将在这一节的后面讨论处理链接的其他巧妙方法。
  • 使用适合介质的单位,如磅(pt)、英寸(in)和厘米(cm)。

images 注意:打印样式表会阻止 Internet Explorer 中的渲染。因此,您可能会考虑推迟用 JavaScript 加载这些内容。

Eric Meyer 在他的优秀文章 List Apart ( [www.alistapart.com/articles/goingtoprint/](http://www.alistapart.com/articles/goingtoprint/))中建议使用:after伪元素将链接到的 URL 附加到它们的链接文本之后。这非常有意义;如果没有 URL,链接文本在打印时实际上是无用的。下面是他实现这一目标的简化代码:

a:after {         content: " (" attr(href) ") "; }

他接着说,这种方法不能很好地处理相对于网站根的链接,并为此提供了一个解决方案:

a[href^="/"]:after {         content: " (http://www.alistapart.com" attr(href) ") "; }

这些解决方案并不完美。相对于当前文档的 URL 仍然不会显示它们的完整路径,并且句末的链接会在右括号后和句号前显示一个空格,但这是对另一种选择的巨大改进。

这是一个打印的列表页示例:

images

图 7-1。列表打印输出示例

印刷和其他分页媒体支持连续媒体的一些额外规则和属性。页面本身可以使用@page在全局级别应用边距,如下所示:

@page {    margin: 2cm 3cm; }

您可以改为将边距应用于body元素,但是这样您就不会在每页的底部获得边距。

左右页也有伪类。打印双面文档时,第二页是“左”页,第三页是“右”页,第四页是“左”,依此类推。在用户将这些页面装订在一起的情况下,通常考虑页面之间更大的装订线区域。这很容易实现:

`@page:left {
   margin-right: 4cm;
}

@page:right {
   margin-left: 4cm;
}`

您还可以使用:first伪类专门针对第一页。第一页既不被视为左侧也不被视为右侧:

@page:first{    margin: 10cm; }

控制分页符

有几个属性的存在是为了让我们更好地控制页面的破损位置。前三个是特定于分页符的:

  • page-break-before
  • page-break-after
  • page-break-inside

page-break-beforepage-break-after接受五个可能的值(如果包括“inherit”,则为六个,这是它们的默认值):

  • auto
    • 这对打印输出没有影响。每当打印机(或分页设备)空间不足时,就会拆分页面。
  • always
    • 在元素之前(或之后,视情况而定)总是强制分页。
  • avoid
    • 设备将尝试避免在元素之前(或之后,视情况而定)分页。
  • left
    • 设备将在前面(或后面,视情况而定)插入分页符,这样下一页将是“左”页(因此可以用:left伪类作为目标)。
  • right
    • 设备将在前面(或后面,视情况而定)插入分页符,这样下一页将是一个“右”页(因此可以用:right伪类作为目标)。

page-break-inside仅支持该列表中的“自动”和“避免”。

images 提示:如果你在表格中使用了 thead、tbody 和 tfoot 标签,页眉和页脚会在每一页上重复,使表格更容易阅读。表格行仍可能被拆分,这可以通过在打印样式表中使用以下 CSS 来避免:

tr {   page-break-inside: avoid; }

您可以在[www.w3.org/TR/CSS2/tables.html](http://www.w3.org/TR/CSS2/tables.html)阅读更多关于表格打印行为的信息。

从可访问性和可用性的角度来看,构建打印样式表是一件容易且值得做的事情。大型网站并不总是迎合打印机,因此给用户的体验很差;采取这一步骤有助于将你的网站与其他网站区分开来。同样,对于一个高流量的网站,越来越多的用户会想要打印你的页面,可能是因为他们没有支持移动互联网的设备。在图 7-2 、 7-3 和 7-4 中有现实生活中的打印样式表的例子。

images

图 7-2。 Gap 不会隐藏不必要的元素或将其文本设置为深色,因此,大部分文本是不可见的,并且第一个打印页面没有有用的内容。

images

图 7-3。维珍的网站在进入任何内容之前就打印出一大堆乱七八糟的东西。

images

图 7-4。《洋葱》让广告妨碍了它的内容。

一如既往的浏览器实现不一致,你还是需要测试你的代码。您可以在打印预览中快速检查您的工作结果,而不是杀死树木。您还可以快速地将您的link标签的媒体类型(或者@import 或@media 规则)切换到“all”,但是请注意,默认情况下,浏览器可能会将不同的样式应用于打印样式表,而不是屏幕样式表。你可以在[www.w3.org/TR/2009/CR-CSS2-20090908/page.html#page-box](http://www.w3.org/TR/2009/CR-CSS2-20090908/page.html#page-box)阅读针对分页媒体的 CSS 2.1 规范。CSS3 增加了更多的功能,允许您控制页码计数器、寡妇行(页面顶部左侧的行)和孤儿行(页面底部左侧的行)。虽然现在的支持是不完整的,但实现这些也无妨。在[dev.w3.org/csswg/css3-page/](http://dev.w3.org/csswg/css3-page/)阅读更多关于他们的信息。

移动设备

你的用户访问你的网站的方法一直在急剧增长。你的许多典型用户现在可能有几种手段来访问互联网:家用电脑,工作电脑,也许是某种机顶盒,和移动电话。手机已经从 20 世纪 80 年代的砖块形状走了很长一段路,现在许多手机本身就是有能力的电脑。他们可以通过许多不同的技术访问您的数据,包括 WiFi、GPRS、2G 和 3G,并且更快的标准一直在出现。对于高流量的网站,将你的数据提供给移动设备的可能性非常高,你的用户会对你有很高的期望。事实上,发展中国家的用户可能会完全绕过电脑,只拥有一台移动设备。平板电脑也在迅速普及,可以被视为移动设备。

向移动设备提供网站服务时,您需要特别考虑一些事情:

  • 延迟:移动 web 开发人员的主要眼中钉,延迟是一个不可避免的问题。无论移动互联网连接速度有多快,延迟很可能永远是个问题。这意味着减少 HTTP 请求比以往任何时候都更重要。
  • 小屏幕尺寸:移动设备的屏幕可能比你习惯使用的小得多。
  • 像素密度:移动设备上的一些屏幕现在有足够高的像素密度(或者对于平板电脑来说足够大),以至于你的媒体查询很容易将它们误认为桌面屏幕。
  • 方向:现在很多移动设备都支持从横向切换到纵向,反之亦然。你的媒体查询应该利用这一点。 7
  • 较低的处理能力:你将渲染卸载到 GPU 的聪明方法,以及你实现的所有过渡和变换不会以每秒三帧的速度给任何人留下深刻印象。
  • 电池寿命:访问网络和做任何 CPU 密集型的事情都会以高于正常的速度消耗用户的电池。
  • 成本:一些用户可能会为他们的带宽付费。他们不会欣赏不必要的网络访问或未经优化的图像。
  • 功能:某些功能(特别是:focus:hoverposition:fixed)可能在这些设备上不可用。

地理定位 2 的一个规范也引入了 DeviceOrientation 事件,当浏览器开始支持它们时,你可以用 JavaScript 把它们挂钩。在 http://dev.w3.org/geo/api/spec-source-orientation.html了解更多信息。

您有几种满足移动用户需求的解决方案。

另一个网站

最佳解决方案是为移动用户提供不同的网站。基于服务器端用户代理检测重定向您的用户(例如,重定向到[m.facebook.com](http://m.facebook.com)[touch.facebook.com](http://touch.facebook.com))允许您基于他们可能的分辨率和他们潜在的和缓慢的网络速度为他们建立完整的体验。不幸的是,除了最流行的设备,这是一件很难实现的事情。首先,该设备上的浏览器制造商(无论是专有的、Opera Mini、Mobile Safari、Firefox Mobile 还是其他浏览器)都付出了大量努力,以确保这些浏览器的用户能够享受良好的体验,即使是在小屏幕上。其次,可用的用户代理一直在变化,每天都有新的代理出现。 8

通过 iPhone 访问时,脸书会将你重定向到完整网站的触摸版(见图 7-5 )。使用accept-language HTTP 头向用户呈现正确的语言(设备正在使用的语言)也足够聪明。


8 Opera 其实伪装成 IE 很多年,以避免服务器端用户代理检测屏蔽浏览器内容。8.02 版改变了这种行为。

images

images

图 7-5。iPhone 上的脸书(上图,人像模式;下图,风景模式)。

你只能现实地瞄准其中最常见的。对于高度和宽度较小的设备,您可以使用媒体查询在显著位置提供移动网站的链接,但请记住,一些最新设备具有非常高的像素密度,可以轻松拥有比台式机更高分辨率的屏幕。如果您确实提供了一个单独的网站,您应该做几件事:

  • 提供移动网站的链接,以防您无法正确检测和重定向用户。
  • 也提供一个正规网站的链接。如果用户遵循它,记住他的偏好。
  • 尽可能以 CSS 精灵的形式提供你的图片。那些 HTTP 请求会对移动浏览器造成很大伤害。
  • 考虑文本链接而不是图像。
  • 考虑单列布局。
  • 瞄准垂直滚动而不是水平滚动。
  • 增加font-size值。
  • 避免依赖任何依赖鼠标的交互,比如:hover
  • 避免依赖任何依赖键盘的交互,比如:focus
  • 触摸屏设备使用:active状态,带按钮的设备使用:focus状态。
  • 避免浮动元素。
  • 请记住,用户可能会改变方向,并迎合横向和纵向。使用百分比和流体布局,而不是固定的像素大小,将有助于你做到这一点。
  • 使用 HTML5 输入类型,如“电话”和“电子邮件”,给设备提示如何帮助用户输入信息。不支持这些类型的设备将优雅地降级为文本。
  • 避免 CPU 密集型操作,例如卸载到 GPU(阅读第八章中的相关内容)、变换、过渡和动画。
  • 尽管使用accept-language头来检测语言是聪明的——使用地理位置 API 来检测位置——避免尝试使用 IP 地址来检测位置。这是出了名的不靠谱。

这种技术的缺点是它需要你维护两个网站。如果您可以使用相同的内容和模板系统来做到这一点,它可能会使实现起来不那么痛苦。这会给你的用户最好的体验。

Pizza Express 向移动设备提供其网站的精简版本,并增加了一些额外的功能,例如检测你的位置,并允许你在浏览器中拨打该餐厅的电话。这是一个很好的例子,说明你应该如何考虑移动设备:不只是打勾,而是给每个人你能给的最好的解决方案,不管是什么设备。

images

images

图 7-6。移动 Safari 上的“披萨快递”网站

使用媒体查询来定位移动设备

您也可以选择使用媒体查询来定位这些设备。请注意,这种方法对性能的影响是相当大的。不仅您的移动用户会因为下载您链接到的所有样式表而受到惩罚,不管应用的是什么样式表(通常,您覆盖的许多图像也是如此),而且您现有的用户群也将被迫下载为移动设备设计的样式表。这增加了贵公司的带宽成本,因为许多文件是毫无目的地提供的。

不支持媒体查询的旧设备将忽略它们或无法解析文件。这使得他们只能使用默认的行为,这通常是足够的(或者至少是被设计成足够的)。

安迪·克拉克在[www.stuffandnonsense.co.uk/blog/about/hardboiled_css3_media_queries/](http://www.stuffandnonsense.co.uk/blog/about/hardboiled_css3_media_queries/)提供了一系列针对各种移动设备的媒体查询,您可能会发现这很有用。但是,不要依赖他们只针对列出的设备。新设备每天都在出现,它们可能满足也可能不满足这些查询的要求。另外,不要同时使用所有这些查询;这将导致大量额外的 HTTP 请求,对移动设备体验的损害更大。如果您有真正的理由针对单个特定设备而不是具有特定功能的所有设备,例如,将 iOS 用户指向您为他们的平台开发的应用程序,请将此视为使用服务器端用户代理检测的唯一有效用途之一。只要有替代方法,就应该避免 UA 检测。

images 注意:你可以通过 tel:协议在网站内点击电话号码,就像这样:

<a href="tel:12345678901">(123) 456-7890</a>

对于已经在使用手机浏览你的网站的人来说,这是一个真正有用的功能。请记住使用国际号码,并以国家代码开始 href 中的号码。

开发一个应用程序

您也可以选择直接为您想要支持的设备构建应用程序。虽然这在某种程度上限制了您的受众,但是您可以利用设备特定的功能和 API,否则您将无法利用它们。你也可以在一个非常封闭的环境中测试你的网站,并且对你的结果更加自信。另一种选择是在应用程序的浏览器中显示您的网站,潜在地将它绑定到更多功能,并保持它作为一个网站直接可用,只在一个地方维护它。

与此类似的是原生网络应用,这是一种制作跨平台应用的可行方法,可能与网站具有相同的代码库,但可以访问原生 API。PhoneGap ( [www.phonegap.com/](http://www.phonegap.com/))包装你的代码,让它成为所有主要移动设备制造商的原生应用。批发应用社区(WAC― [www.wacapps.net](http://www.wacapps.net))和设备应用编程接口和政策工作组(DAP― [www.w3.org/2009/dap/](http://www.w3.org/2009/dap/))都不成熟,但是值得关注。

其他设备

您还应该考虑其他设备,这些设备可能是您的目标,也可能不是。内置网络浏览器的机顶盒、游戏机、智能电视中的原生浏览器、谷歌电视等等。其中每一个都有自己的功能和特点。我们建议尽一切可能进行测试。

搜索引擎优化(SEO)

搜索引擎蜘蛛(通过“抓取”所有可以找到的链接并将结果发送回搜索引擎来扫描您的网站的程序)本身可以被视为设备。他们有自己独特的用户代理,和自己独特的能力。搜索引擎优化(SEO)是确保这些蜘蛛有最好的机会到达你的所有内容,并且当它们到达时尽可能合适的做法。搜索引擎优化对大公司来说非常重要,因为它可以带来大量的流量,从而带来大量的销售额(或者不管你网站的目标是什么)。SEO 和可访问性一样,需要从一开始就考虑,并以一致和理智的方式实现。

讨论 SEO 技术的任何细节都应该有自己的书,但是我们将在这里提到一些高层次的观点。首先,我们将讨论白帽、灰帽和黑帽技术之间的区别:

  • 白帽子:这些技巧是出于善意和无私的目的。考虑到 SEO,这通常意味着编写好的内容并在适当的时候链接到它。搜索引擎喜欢这类内容;这有助于他们确保搜索结果与搜索词相符,这也正是他们的用户想要的。
  • 灰帽子:这些技术可以被认为是试图欺骗系统,但同样是在没有其他好的选择时出于善意使用的。一个例子是使用text-indent:-9999px;隐藏浏览器中的文本,但不隐藏屏幕阅读器中的文本(如前一章所讨论的)。这种技术也可以很容易地用于向页面填充网站作者想要排名的关键词,但对用户隐藏它们。搜索引擎通常会接受这种方法,而不会对有问题的网站造成不利影响(因为,目前,这是我们保持可访问性的最佳方法之一),但不会对受其影响的单词给予额外的权重。
  • 这些技术是彻头彻尾的欺骗系统的企图。例如,加载一个填充了您想要排序的关键字的页面,然后用 JavaScript 重定向(门口页面),使用服务器端用户代理检测向搜索引擎呈现一个与您的用户完全不同的页面(隐藏),将关键字放在白色背景上的白色文本中(不可见文本),等等。使用这些技术将会导致你的网站受到惩罚,并在搜索引擎结果中比其他情况下显示得更晚。在极端的例子中,这可能导致你的网站被禁止在搜索结果中出现。

许多人认为他们已经找到了新的和聪明的方法来欺骗搜索引擎。事实上,他们所做的一切都是在浪费时间,给用户更糟糕的体验,浪费本可以更好地用于改善网站的时间,推迟不可避免的事情。从长远来看,搜索引擎会发现你在做什么,并因此惩罚你。事实是,唯一一个确切知道什么会在搜索引擎的算法中排名靠前的是搜索引擎。即使这样,您的请求也可能被定向到许多具有不同算法版本的服务器中的一个,并且算法可能会经常改变。然而,还是有一些通用的指导方针。为了获得好的搜索引擎排名,重要的是

  • 拥有优质内容。
  • 确保你的内容是语义和有效的。
  • 确保你有良好的网站表现。

真的,这与制作高质量的网站是一致的,这也是我们希望你的目标。拥有好的内容是搜索引擎最重视的。使您的内容具有语义和有效性(包括使用微格式、公共类名和 id)有助于搜索引擎理解内容和您的意图,并在搜索结果页面上以更全面、更有吸引力的方式呈现内容。

根据搜索引擎的不同,蜘蛛作为设备有不同的功能。有的解析 JavaScript,有的不解析;有些解析 CSS,有些不解析。总的来说,搜索引擎现在理解并应用 CSS 到页面上。通常情况下,如果他们认为文本由于某种原因(灰色帽子)是不可见的,他们不会对你的页面的内容或其中的单词进行排名。如果您使用 JavaScript 来显示和隐藏信息(例如,在选项卡式的框中),这意味着在页面加载时不活动的选项卡将不会对其内容进行索引。如果搜索引擎支持 JavaScript,那么当抓取页面上的链接时,如果特定链接使内容可见,搜索引擎可能会选择抓取该内容,尽管它可能认为它不如最初显示的文本重要。

出于(至少)这个原因,你应该在禁用 JavaScript 的情况下,让你的网站以合理的方式显示。对于选项卡式框,将标记中的每个选项卡显示为一个标题,选项卡的内容直接位于其下是有意义的。当页面加载时, 9 您的 JavaScript 可以根据需要重新排列页面,并附加正确设置选项卡所需的类。正如我们之前提到的,这也是一个很好的可访问性方法;如果你使你的网站具有语义和可访问性,你可以免费获得一些搜索引擎优化。

images 注意:搜索引擎(几乎)永远不会提交表格。

搜索引擎越来越看重的另一件事是你网站的表现。如果你的网站下载速度很快并且通过了验证,你的排名会轻松提升。如果你一直遵循本书中的指导方针,希望你的 CSS 小而快,并且页面上没有太多的 HTTP 请求。使用内容交付网络(CDN——见第八章)也能提高你的速度,这是搜索引擎会欣赏的。


JavaScript 可以挂钩到一个“DOM 就绪”事件,该事件在图像加载之前、DOM 加载之后触发。此时对文档进行更改(而不是在下载完文档上的所有内容后触发的“load”事件)将避免非样式内容(FOUC)的闪现,这可能会非常令人不安。

总结

本章旨在向您介绍一些用户可能会使用的设备,以及如何最好地锁定他们。媒体类型和媒体查询通常是有用的,但它们并不是我们中的一些人所希望的灵丹妙药。在这些情况下,努力让你的网站易于阅读和浏览将会有所回报。

下一章关注的是 CSS 的性能,并打算教你 HTTP 请求和响应是如何工作的,缓存策略,以及如何最好地利用两者。

八、性能

CSS 文件通常是语法简单的小文件。看起来,就性能而言,几乎没有什么可做的,改进也是微不足道的。在很多情况下,这可能是真的。然而,当我们以更大的规模处理 CSS 时,我们的文件可能会变得更大,并且我们希望它们每天被提供数百万或数千万次,小的改进会产生大的差异——对用户和开发人员都是如此。以今天的标准来看,一千字节的数据量似乎很小,但是算一算,这些千字节很快就会增加到千兆字节的数据量,企业需要为此支付带宽费用。当我们考虑用户下载的有效负载和页面渲染的速度时,可以说每一点都很重要。

在关注性能时,我们必须考虑几个角度。从用户的角度来看,重要的是文件要小,可以很好地缓存(因此加载更快),并且文件是最新的。从浏览器的角度来看,我们希望我们的 CSS 尽可能高效,并且尽可能快地呈现内容(如果需要,响应交互或动画而重新呈现)。从企业的角度来看,我们希望尽可能多地从用户的缓存(主要是我们的服务器缓存,其次是我们的服务器缓存)提供服务,并将我们发送给用户(和从用户接收)的数量保持在最低水平,同时仍然确保用户拥有我们代码的最新版本。

在这一章中,我们将集中讨论如何从这三个不同的角度提高性能,并且您将了解到关于每一个方面的一些重要的事情。您将了解以下内容:

  • 文件大小问题和最佳实践
  • 拥有更少的 HTTP 请求比文件大小更重要
  • 缓存策略
  • 浏览器渲染和阻止

有效负载—担心文件大小

CSS 中的最佳实践要求我们考虑输入的字符数量以及它们的含义。每个字符都很重要。虽然现在高速互联网越来越普及,但作为一个高流量网站的 CSS 作者,你要担心的人口统计数据要比大多数其他公司多得多。因此,您的用户可能在拨号上网(出于选择或由于他们的位置),在接收信号差的地区使用移动设备,在远离您的站点(您的服务器)的国家,或这些情况的任何组合。预处理可能发生在许多级别,例如他们所在位置的 ISP、防火墙和路由器,或者数据路径上更高级别的防火墙和路由器。我们的数据尽快到达这些机器的主要问题之一是我们发送的数据量。当我们通过 TCP/IP(传输控制协议和互联网协议),互联网通常使用的网络协议发送数据时,我们的信息被分组为称为的包。在网络上有一个最大数据包大小的概念,或最大传输单元(MTU),在以太网上通常是 1500 字节。超过这个大小的数据包会导致性能损失(损失有多大取决于许多因素:MTU、数据包大小、网络速度等)。由于我们无法确定任何特定网络的 MTU 是多少——即使我们知道,也很难知道哪个数据包会超出限制——为了避免这种数据包边界,我们所能做的就是尽最大努力提供尽可能少的数量。

比方说,互联网用户比购物中心的用户更易变。决定一个网站太慢并且浏览到另一个网站提供了即时的满足感,而找到另一个出售平底锅的商店需要用户承诺离开当前的商店,找到另一个商店,找到产品,等等。用户对我们网站的好感是有限的,我们必须尽一切努力让用户满意,并朝着我们的商业目标前进——无论是购买产品或服务、浏览页面还是消费内容。

保持文件大小和速度不仅对我们的访问者有好处。这对我们的业务也有好处——我们提供的数据越少,我们产生的带宽成本就越少。此外,谷歌和其他搜索引擎关心我们的网页加载速度。网站的性能正迅速成为有效的 SEO 策略的一个重要因素。此外,Internet Explorer (IE)版本 7 和更低版本无法处理 288 KB 以上的 CSS 文件。

images 注意:对于超过 288 KB 的 CSS 文件,IE 7 及以下只会解析文件的前 288 KB。尽管可以将比这个大的文件分成多个文件(更多的请求会对性能产生影响),但显然最好首先保持我们的 CSS 较小。

那么,我们能做些什么来降低文件大小呢? 1

命名惯例

正如我们以前说过的,制定关于类和 id 如何命名的规则,并严格遵守这些规则是很重要的。除非有很好的理由不在您的组织中这样做,或者您的开发人员特别反对这样做,否则我们建议使用 camel case 命名您的类和 id,如下所示:

  • 主要内容
  • 英雄形象
  • 电子邮件地址

使用 camel case 应该有助于使我们的代码易于阅读,同时避免连字符、下划线或其他分隔符带来的额外字符。如果通过前缀给代码命名空间,尽量避免像这样冗长的前缀:

  • ourCompanyHeroImage
  • 我们公司的电子邮件地址

1 谷歌在[code.google.com/speed/pagespeed/docs/payload.html](http://code.google.com/speed/pagespeed/docs/payload.html)提供了一个让你的有效载荷最小化的指南。雅虎!提供自己的指南,以最大限度地提高您的网站在[developer.yahoo.com/performance/rules.html](http://developer.yahoo.com/performance/rules.html)的性能。

相反,考虑缩写或首字母缩略词;例如:

  • 赭石图像
  • ocEmailAddress

如果这些名字很难读,我们现在可以考虑使用一个分隔符,安全地知道我们的前缀仍然比其他的更小。

  • oc-HeroImage
  • oc 电子邮件地址

尽管我们希望我们的命名系统是语义化的,但我们也可以考虑将其缩写,只要它对开发人员来说仍然易于阅读:

  • 海洛因
  • 电子邮件地址

这种方法的缺点是单词有许多可接受的缩写,开发人员可能会发现命名不太直观,导致代码来回切换,或者命名技术不一致。我们建议(一如既往地)在您的组织内讨论什么是可接受的,以获得最佳结果,将该技术添加到您的 CSS 编码标准指南中,并严格执行。

文件名

在我们的代码中,文件名用于将文件链接在一起并引用它们。为了保持我们的文件非常小,将我们的文件命名为 a.css、b.css 等等是很有诱惑力的。事实上,只要文件使用正确的 mime 类型, 2 我们甚至可以将它们命名为a . c .或者仅仅是 a 。然而,实际上,我们认为这在易读性上造成了太多的成本,使得代码难以编写,文件难以定位。尽管文件名不应该太长,但是它们应该足够长,以便于理解。下面是一个在 CSS 中引用文件的例子:

input {background: urlimg/shadow background.gif");}

URL 中通常不需要双引号。因为文件名中有空格,所以将它们包含在本例中,这意味着需要引号,以便浏览器理解空格是文件名的一部分。但是,在命名文件时,我们建议您始终使用连字符而不是空格。我们建议这样做有几个原因:

  • Google 会将 URL 路径中的连字符视为空格,而不是其他字符。如果我们将我们的文件命名为“shadow background.gif”,Google 会将其理解为“shadow background.gif”。对用于展示的图片而不是内容(如背景)进行良好的 SEO 可能看起来没有必要,但谷歌图片搜索占据了惊人的流量,任何可能将用户带到我们网站的东西都是积极的事情。
  • 不使用空格意味着我们不需要 URL 值中的引号,节省了两个字符。
  • 使用空格意味着浏览器需要智能地处理它们,并在 URL(一个空格,URL 编码)中将它们更改为%20,而较旧的浏览器可能不支持这一点。当然,您可以将编码的空格直接放在 URL 中,但是这将导致每个实例中多两个字符。

2 A mime 类型是一个响应头,标识所提供文件的类型。CSS 文件的 mime 类型是“text/css”。

我们还建议您始终用小写字母命名文件,即使使用不区分大小写的 web 服务器/操作系统。这是一个容易遵循的规则,如果你以后决定更换网络服务器,它会使你的文件更容易移植。避免使用符号和不常见的字符还可以简化从一个服务器到另一个服务器的转换,并避免意外问题。尽管在文件名中使用点号已经成为版本控制的常见做法(如在 jquery-1.4.4.min.js 中),但是您应该避免将它们放在文件名的开头,因为基于 Linux 的操作系统会将其解释为隐藏文件。作为最佳实践,最好坚持使用拉丁字母数字字符、连字符和点。

文件夹结构

乍一看,文件夹结构似乎对文件大小没有什么影响。然而,这是不真实的。文件夹经常在整个 CSS 代码中被引用,无论是指向背景图像、字体、行为还是导入其他样式表。 3

通过引用文件夹来最小化字符消耗的最简单的方法是将所有文件保存在与 CSS 相同的目录中。这种技术在任何规模的网站中都会很快变得不可用,然而,和往常一样,我们应该在什么是好的实践和什么会让开发人员的生活成为噩梦之间寻求一个好的平衡。

下面是一个在 CSS 中引用文件的例子:

input {background: urlimg/shadow background.gif");}

我们的 URL 中的第一个字符是一个正斜杠,这意味着从 URL 的根开始。所以,对于一个位于[www.thedomain.com/css/style.css](http://www.thedomain.com/css/style.css)的 CSS 文件,这个 URL 会指向[www.thedomain.cimg/shadow%20background.gif](http://www.thedomain.cimg/shadow%20background.gif)。CSS 中的所有 URL 都是相对于包含 CSS 的文件所在的位置,所以假设我们将 CSS 更改为以下内容(省略了斜杠):

input {background: url("img/shadow background.gif");}

该 URL 将指向[www.thedomain.com/cimg/shadow%20background.gif](http://www.thedomain.com/cimg/shadow%20background.gif)

因为我们用 CSS 引用的任何文件主要是供 CSS 使用的,所以将它们保存在我们的 CSS 所在的同一个文件夹中的一个子文件夹中是有意义的。这使得我们的 CSS 更加可移植,因为我们可以复制或移动 CSS 文件夹,并且所有的文件路径和引用都将保持不变。它还为我们节省了 URL 中的正斜杠,或者../,跳到一个文件夹,找到我们要找的文件。

通常,文件夹是以它们所代表的复数来命名的,例如:

  • 形象
  • 资产
  • 字体

3 行为通过 HTML 组件(HTC)文件实现,IE 版本 5 及以上支持。它们允许您通过 JavaScript 为元素分配特定的行为。因为它们引入了对 JavaScript 的依赖,并且只限于 IE 浏览器,所以我们不推荐你使用它们。

通过始终使用单数来代替,很容易节省字符并在我们的业务中实现这种命名技术。在文件夹名称中,使用缩写要安全得多,因为我们的整个文件夹结构(至少在高层)很可能是非常可预测的,并且从一个项目到另一个项目完全相同。这是另一件你应该添加到你的 CSS 编码标准指南中的事情:

  • 图片
  • 资产
  • 字体

我们的行最初是 58 字节,如下所示:

input {background: urlimg/shadow background.gif");}

它现在是 52 字节,如下所示:

input {background: url(img/shadow-background.gif);}

这减少了 10%。这可能看起来是一个小变化,但是如果我们的文档中有 100 行类似的代码,我们现在就可以节省 60 个字节,如果考虑到我们要响应的请求的数量,这意味着更多。

您还应该在文件名中随意使用常见且易于理解的缩写,这可以进一步减少我们的 CSS:

input {background: url(img/shadow-bg.gif);}

我们的资产文件夹只是一个总括文件夹,用于存放我们想要引用的不容易归入另一个分组的任何文件,如 Shockwave Flash (SWF)、多媒体 Adobe Flash 文件。记住所有这些,让我们来看一个文件夹命名策略的例子:

   /       css          img          font          asset       img       js       asset

如果您想一想我们引用这些文件的所有地方,这一切都转化为更小的文件、更快的下载和更少的带宽。

语法

为了易读性,我们在 CSS 语法中加入了许多不必要的部分。因为这些通常可以通过缩小工具被最小化(在这一章的后面会读到更多),如果你正在使用它们的话,在你写的时候把它们保存在你的 CSS 中是安全的。然而,了解这些及其含义是很重要的。

空白

CSS 实际上需要很少的空白字符。选择器各部分之间的间距是必要的,正如速记 CSS 中引用的项之间的间距,或者将多个部分作为其值的属性之间的间距一样。其他一切的存在纯粹是为了让我们作为开发人员更容易阅读和扫描我们的代码。

请注意以下代码:

`body
{
        font-family: arial, sans-serif;
        font-size: 16px;
        background-color: #f4f4f4;
}

.clear
{
        clear: both;
}`

从浏览器的角度来看,前面的代码与此完全相同: 4

body{font-family:arial,sans-serif;font-size:16px;background-color:#f4f4f4;}.clear{clear:both;}

虽然第二个例子更难阅读,但它大大节省了文件大小。其中一些值得简单解释一下:

  • 选择器和左大括号之间的空间是不必要的。
  • 属性名后面的冒号和值之间的空格是不必要的。
  • 逗号分隔值之间的空格是不必要的。
  • 右大括号后的回车是不必要的。
  • 空格在前!重要的都是不必要的(以及任何介于!而且重要)。

在所有这些情况下,您可以使用其他空白来代替空格,比如制表符。如你所见,这里有很多储蓄。我们不建议你用这种方式编写你的 CSS,因为这是不可能管理的(参见本章后面关于缩小你的代码的部分)。

images 注意:当使用名称中带有空格的字体时,你应该根据 CSS 规范用引号(或者是双引号或者是单引号)将它们括起来。尽管许多浏览器对省略引号没有问题,但其他浏览器不能正确读取值。


4 虽然大多数浏览器不区分字体名称的大小写,但一些老的浏览器会区分。值得注意的是,Adobe Flex([www.adobe.com/products/flex/](http://www.adobe.com/products/flex/)—用于开发 Adobe Flash 和 Adobe AIR 应用程序的框架)存在字体名称大小写不正确的问题。

最后一个分号

最后一个属性之后和右大括号之前的分号是不必要的,在不影响可读性的情况下,很容易省略。

请注意以下几点:

.clear {clear:both;}

以及以下内容:

    .clear {clear:both}

就浏览器而言,这两行完全相同,并且为每个规则保存一个字符。

背景颜色

当指定背景颜色时,不要(以更正确的方式)陈述如下:

background-color: blue;

使用以下代码是安全的:

    background: blue;

这在每个实例中节省了六个字符。但是请注意,这种技术会覆盖这个速记属性中设置的其他属性,可能会产生意想不到的效果。

零和单位

因为无论使用什么单位,零都是零,所以在这种情况下没有必要使用单位。请注意这两个属性:

border-width: 0px; margin-top: 0em;

它们可以安全地写成如下形式:

    border-width: 0;     margin-top: 0;

这对页面的呈现没有任何影响。

取消边界

移除边框时,不要声明以下内容:

border-width: 0;

或以下内容:

    border: none;

相反,您可以安全地放置以下内容:

    border: 0;

同样,从浏览器的角度来看,这导致没有边框和更少的字符。

零和小数位

当使用从零开始的小数位时,可以安全地省略零。换句话说,不是陈述如下:

font-size: 0.12em;

安全的做法是改为使用以下内容:

    font-size: .12em;

这两个语句是完全等效的,并且在每个实例中都保存了一个字符,尽管对于一些开发人员来说它们可能更难阅读。这个动作是由大多数 CSS 缩小工具代表你采取的,所以如果你发现易读性受到影响并且你正在使用这些工具,保持零是安全的。

空白/填充速记

请注意以下 CSS:

margin-left: 10px; margin-right: 10px; margin-top: 10px; margin-bottom: 10px;

这也可以表示为(按上、右、下、左的顺序):

    margin: 10px 10px 10px 10px;

它也可以(因为所有四个值都相同)表示为:

    margin: 10px;

如果提供三个值,第一个代表顶部,第二个代表两个水平值,第三个代表底部。请注意以下 CSS:

padding-left: 5px; padding-right: 5px; padding-top: 20px; padding-bottom: 10px;

与此相同:

    padding: 20px 5px 10px;

此外,如果只为 margin 或 padding 属性提供两个值,它们分别代表垂直和水平值。请注意以下 CSS:

padding-left: 10px; padding-right: 10px; padding-top: 20px; padding-bottom: 20px;

这完全等同于:

    padding: 20px 10px;

也完全等同于这个:

    padding: 20px 10px 20px;

还有这个:

    padding: 20px 10px 20px 10px;

对于边框、边框半径、列表样式、字体以及其他许多属性,还有许多其他的速记属性,它们不在本书的讨论范围之内。值得花些时间研究和记录它们,并确保团队中的其他成员能够熟练使用它们。除了使你的 CSS 文件更小之外,它们还能使你的 CSS 更容易阅读和扫描。但是,您需要注意这种方法的注意事项,因为速记属性会覆盖组成它的所有子属性,可能会产生意想不到的后果。

颜色

每个浏览器都有一系列命名的颜色(在 HTML 3.0 的规范中定义),它可以理解这些颜色,并适当地呈现出来。其中包括以下: 5

  • 浅绿色
  • 黑色
  • 蓝色
  • 紫红色
  • 灰色
  • 绿色的
  • 石灰
  • 褐红色
  • 海军
  • 橄榄
  • 紫色
  • 红色
  • 水鸭
  • 白色
  • 黄色

5CSS 2.1 规范中也包含了颜色“橙色”,定义为#ffa500。背景色的 CSS1 规范中包含了颜色“透明”,它没有十六进制颜色的对等物。CSS2 使该属性适用于边框颜色,CSS3 将其定义为适用于任何接受颜色值的属性。

浏览器支持更多的颜色,但它们不是 W3C 标准的一部分([www.w3.org/TR/css3-color/#html4](http://www.w3.org/TR/css3-color/#html4))。然而,这些颜色中只有八种是“绝对”颜色,这意味着它们的红色、绿色和蓝色值都是零或 255。这些是如下: 6

  • 浅绿色(#00ffff)
  • 黑色(#000000)
  • 蓝色(#0000ff)
  • 紫红色(#ff00ff)
  • 石灰(#00ff00)
  • 红色(#ff0000)
  • 白色(#ffffff)
  • 黄色(#ffff00)

在这些颜色中,只有下列颜色不可解释:

  • 黑色(#000000)
  • 蓝色(#0000ff)
  • 红色(#ff0000)
  • 白色(#ffffff)
  • 黄色(#ffff00)

有趣的是,绝对的“绿色”被认为太亮而不能被描述为绿色,因此被命名为“石灰”;“绿色”作为关键词代表#008000,比绝对的绿色暗很多。

因此,我们建议在您的 CSS 中只使用这些。它们更容易阅读和理解,并且在许多情况下比它们的十六进制对应物的字符更短(除非你使用速记颜色(下面提到);在所有情况下,它们都比它们的 RGB 对应物短)。

但是,还可以实现进一步的节约。以十六进制表示的颜色(称为“十六进制颜色”)被指定为三个一组(意味着它们由三部分组成)。三元组的每个部分都是一个十六进制字节:一个有 256 个变量的值,在本例中从 0 (00)到 255 (ff)。三联体本身有 16,777,216 种变化。如果三联体的每个部分都由两个相同的字符组成,我们可以将它们缩短为一个。例如:

  • 112233 可以表示为#123

  • 00aaff 可以表示为#0af

  • 99aa00 可以表示为#9a0

使用命名颜色或十六进制颜色的决定将在您的组织内做出。在使用十六进制颜色的地方,使用简写版本是安全的。您可以决定在适当的情况下使用命名的颜色,因为它们更容易阅读,但是任何经验丰富的 CSS 开发人员都会习惯于看到#000 和#fff,因此在这种情况下坚持使用十六进制颜色没有问题。虽然 RGB 颜色是在 CSS2.1 规范中定义的,但 CSS3 扩展了这一点,以支持 RGBA 以及 HSL(色调、饱和度、亮度)和 HSLA(色调、饱和度、亮度、Alpha)。这些接受非十六进制值 RGB 值是介于 0 和 255 之间的整数,Alpha 是介于 0(透明)和 1(不透明)之间的数字。色调以度为单位(0 到 360),而饱和度和明度是百分比。虽然所有这些值都比十六进制颜色更冗长(并且增加了更多的文件大小),但是您可能会发现实现它们更有用。

images 注意:如果在 CSS 中使用 RGBA、HSL 或 HSLA 颜色定义,请确保为不支持它们的浏览器提供 RGB 或十六进制颜色回退。此外,如果您在 IE 中使用filter属性(可能是为了跨浏览器支持渐变、不透明等等),请注意该属性不支持速记颜色、RGB、RGBA、HSL 或 HSLA。一些较老的移动浏览器对这样的简写颜色有问题。然而,这些浏览器几乎已经消失了,即使是流量最高的网站,我们也不建议关注这一人群。

作为使用这些语法方法来节省文件大小的最后一个例子,请参见下面的 CSS,重量为 146 字节:

.our-company-main-content {    background-image: url("../img/shadow background.gif");    border: none;    margin-top: 0px;    color: #aabbcc; }

这可以很容易地缩短到 110 字节:

.oc-mainContent {    background-image: url(img/shadow-background.gif);    border: 0;    margin-top: 0;    color: #abc }

可读性没有损失,但是我们节省了 36 个字节,减少了 25%——甚至在缩小文件之前。

最小化图像尺寸

了解什么样的图像类型适合什么样的情况以及如何缩小图像大小至关重要。在接下来的部分中,我们将讨论您将使用的三种主要类型的图像。

GIF(图形交换格式)

gif 最多支持 256 种颜色,并支持透明,但不支持 alpha 透明。这意味着,通过使用我们可用的 256 种颜色中的一种,我们可以将像素设置为完全透明或完全不透明(不透明),但不能介于两者之间(半透明)。它们通常适用于按钮图像、具有硬边的项目以及颜色准确性非常重要的图像。gif 是一种无损压缩格式,这意味着只要您使用 256 色或更少的颜色(包括透明度),图像质量就不会下降。当保存 gif 时,使用你的图像编辑器来确保你最小化使用的颜色数量,并且如果你不需要的话关闭透明度。gif 可以包含多个帧和信息,以在这些帧之间进行动画制作,并在这些帧之间循环播放简单的动画(这会显著增加文件大小,应谨慎使用)。

联合图像专家组

JPEG 压缩是有损的,这意味着图像压缩得越多,信息就丢失得越多。您的图像编辑器应该包括一个滑块或类似的工具,用于在导出图像时调整压缩量。当您降低质量(提高压缩率)时,文件大小会减小,但图像中会出现伪像。由设计者决定适当的和视觉上可接受的压缩水平,通常 80%到 90%是最好的折衷,并且应该产生不明显的伪像。JPEGs 支持 1600 万种颜色,最适合照片或多种颜色的复杂图像。JPEGs 完全不支持透明。

PNG(便携式网络图形)

PNG 是一种无损格式,通常具有比 GIF 文件更好的压缩率。它们可以保存为不同的颜色深度,这意味着它们可以支持适合当前图像的任意多种颜色。它们还支持 alpha 透明,这意味着像素可以是半透明的。png 通常适合作为 gif 的替代品,尽管动画 png 还没有得到广泛的支持。保存 png 时,将颜色深度设置为适合图像的值是很重要的。

images 注意:虽然 IE 6 不支持 alpha 透明,但是有很多黑客可以使用 JavaScript、行为文件、过滤器以及它们的混合来解决这个问题。我们推荐的方法是 Fireworks hack,或 8 位 PNG hack。如果您将 Adobe Fireworks 中具有 alpha 透明度的 PNG 保存为 8 位(这实际上是软件中的一个错误),结果是它可以在所有浏览器中正常显示,但 IE 6 除外,它可以使任何半透明像素完全透明。这通常是一个可以接受的折衷方案,只要你的设计者考虑到这个约束。

您的图像编辑器应该有工具来帮助您最小化这些文件的文件大小,并查看各种文件格式、压缩率和颜色深度的差异。然而,即使在这之后,在您的图像文件中也可能有无关的信息。注释、元数据和不必要的颜色配置文件可能会留在文件中。有许多单独的工具可以解决这些问题,但 ImageOptim(可在[imageoptim.pornel.net/](http://imageoptim.pornel.net/)获得)是一个面向 OS X 的免费开源应用程序,它结合了所有这些工具。它通常可以节省 30%到 35%的文件大小。在您的构建过程中实现这样的工具是一件值得做的事情,应该认真考虑。可以使用各种 Windows 和 Linux 替代工具(尽管功能不太全面),但是在构建过程中使用多个命令行实用工具应该能够获得相同的结果。

缩小

使用本章到目前为止概述的技术,编写你自己的 CSS 缩小脚本并不困难,它可以代表你自动完成所有的工作。然而,在这个过程中有很多事情会让你措手不及。幸运的是,已经有方法可以帮你做到这一点。其中最常见的是 YUI 压缩机。

YUI 压缩器是一个基于 Java 的工具,用于缩小 JavaScript 和 CSS 文件,这些文件可以在[github.com/yui/yuicompressor/](http://github.com/yui/yuicompressor/)下载。它代表我们自动执行任务,照顾到我们在本章中已经讨论过的最小化文件大小的许多方法,这意味着我们可以在开发代码中忽略它们,但仍然可以在生产代码中获得好处。一旦安装,语法非常容易使用。在命令提示符下,语法如下(其中 x.y.z .是版本号):

java -jar yuicompressor-x.y.z.jar [options] [input file]

适用于 CSS 的潜在选项有:

--line-break

这个选项允许我们在一个特定的列之后将 CSS 分成几行(在这个例子中,表示一行中的一个特定字符)。例如,将此项设置为 1000 将在每行的第一千个字符后插入一个换行符。在很多情况下你可能需要这个选项,特别是在调试的时候(你可以在第十章中读到更多关于调试的内容)。将此项设置为 0 将在每个规则后插入一个换行符。

--type

该选项允许我们指定输入文件的格式。只有当我们的文件后缀不是。css(或者。js),在这种情况下,对于我们的 css 文件,我们应该总是将它设置为 CSS。

--charset

这个选项允许我们指定输入文件的字符集。如果不提供,将使用运行 YUI 压缩程序的系统的默认字符集编码。虽然在大多数情况下没有必要,但如果您使用 UTF-8 字符(例如,在内容属性中),则应该设置此选项。

-o

此选项指定我们要输出到的文件的名称。

以下是一个命令示例:

java -jar yuicompressor-x.y.z.jar style.css –o style.min.css

YUI 压缩程序对 CSS 文件执行以下功能:

  • 一条条评论 7
  • 删除不必要的空白
  • 删除每个规则的最后一个分号
  • 删除任何多余的分号
  • 删除任何没有属性的规则
  • 移除任何零值上的单位
  • 删除任何小数位以零开始的值的前导零
  • 在边距、填充和背景位置将相似的属性合并为一个
  • 将任何 RGB 颜色转换成十六进制颜色 8
  • 删除无关字符集 9
  • 使用 filter 属性将 alpha 不透明度语句缩短为 IE4 样式 10

如果出于某种原因,你想保留特定的评论(也许是出于版权或许可的目的),你可以在评论的开头使用感叹号,就像这样:

/*! This comment is far too important to be removed: */

正如本章后面提到的,当使用 CSS 过渡时,在一些浏览器中使用十六进制颜色而不是 RGB 颜色会有性能损失,所以意识到这一点尤为重要。

如果您使用的是 YUI Compressor,您可以放心,因为所有这些操作都将被执行,而且,如果您愿意,也不用担心它们会出现在您的未统一代码中。

如果您使用过任何黑客攻击(其中许多在第三章中有详细介绍),了解 YUI 压缩机容忍其中许多攻击并且不会试图缩小或破坏它们是很有用的。公认的方法包括:

  • 下划线黑客
  • 明星黑客
  • 子选择器黑客
  • 注释的反斜杠 hack
  • 盒子模型黑客

如果您不希望使用 YUI 压缩器的 Java 实现,可以在[www.phpied.com/cssmin-js/](http://www.phpied.com/cssmin-js/)找到一个 JavaScript 端口。还有许多缩小 CSS 的其他选项,包括:

  • [www.csscompressor.com/](http://www.csscompressor.com/)
  • [www.cleancss.com/](http://www.cleancss.com/)
  • [www.cssdrive.com/index.php/main/csscompressor/](http://www.cssdrive.com/index.php/main/csscompressor/)

9 可以用如下命令定义 CSS 中的字符集:

@charset "utf-8";

任何 CSS 文件只能包含一个@charset 语句。

10 滤镜属性(Internet Explorer 专有)允许你设置各种视觉效果。要设置不透明度,建议的语法是:

selector { -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=65)"; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=65); }

filter 属性适用于 IE 8 和更低版本。–ms-filter 属性适用于 IE 9 及更高版本。YUI 压缩器将这些简化为它们的速记等价物,如下所示:

selector { -ms-filter:"alpha(opacity=65)"; filter:alpha(opacity=65); }

由于 YUI 压缩机的广泛使用,它是我们推荐的产品。使用它并为社区做出贡献的开发人员的数量意味着它是一个非常健壮且经过良好测试的解决方案。无论你做什么决定,我们都建议你对 CSS 代码使用一个缩小工具,尽管这意味着调试(你可以在第十章阅读更多关于调试的内容)是可以克服的。

压缩

在我们尽可能地将文件变小之后,我们仍然可以使用一些技巧来使从服务器传输给用户的数据变得更小。其中之一是压缩。文件的压缩涉及到使用算法来存储重复的数据,并且只表示该数据一次而不是多次。有两种主要的压缩方式:无损压缩和有损压缩。有损压缩意味着在压缩过程中会丢失一些信息,JPEG 就是有损压缩格式的一个例子。我们对 JPEG 图像压缩得越多,出现的伪像就越多,离原始图像就越远。这对于图像格式来说是可以接受的,因为我们试图传达的内容并没有完全丢失。然而,对于 CSS 文件,任何引入数据的工件都会破坏我们的语法,使我们的文件变得不可预测和无用。无损格式,如 zip、tar 和 rar 文件,是我们需要的每个字符都很重要的数据。

HTTP 1.1——用于交付网页的协议——在其规范中支持三种压缩方法:gzip (GNU zip)、deflate 和 compress。浏览器和服务器对 deflate 和 compress 的吸收比 gzip 慢。所有现代浏览器和服务器都支持数据的 gzip 压缩,因此,它已经成为在互联网上压缩数据的行业标准技术。 11

下面显示了从浏览器向服务器发出请求以及服务器做出响应的示例流程:

  • The browser makes a request to the server, and includes headers similar to these: GET /index.html HTTP/1.1 Host: www.domain.com Accept-Encoding: gzip User-Agent: Firefox/3.6

    这些行依次表示以下含义:

    • 浏览器正在使用 HTTP 1.1 协议请求文件/index.html。
    • 浏览器指定它想要文件的域。
    • 浏览器表明它支持 gzip 编码。
    • 浏览器将自己标识为 Firefox 3.6。
  • 服务器找到该文件并将其读入内存。

  • 如果浏览器表明它支持 gzip 压缩(在本例中就是这样),服务器就会压缩文件。

  • The server returns the data, with headers similar to the following: HTTP/1.1 200 OK Server: Apache Content-Type: text/html Content-Encoding: gzip Content-Length: 12345

    这些行依次表示以下含义:

    • 服务器确认它正在使用 HTTP 1.1 作为协议,并发送一个 200 状态码来表示一切正常。
    • 服务器将自己标识为 Apache。
    • 服务器将响应的内容识别为 HTML。
    • 服务器通知浏览器数据是用 gzip 压缩的。
    • 服务器指定它返回的数据的大小,以便浏览器可以显示一个进度条,并确定何时收到所有数据。
  • 浏览器解压缩数据,并读取它。


11 你应该知道,使用 gzip 时,由于压缩算法需要考虑单个字符,因此对于较小的字符集,压缩率会更高。提高性能的一个简单方法是尽可能使用小写字符,尽管这不如减少字符重要。

使用最新的 web 服务器,在服务器上实现 gzip 压缩很容易,您将在接下来的几节中看到这一点。

Apache

mod_deflate 是 Apache 用来压缩文件的模块。它包含在 Apache 2.0.x 源代码包中。安装 Apache 后,编辑适当的。conf 文件,并确保以下行存在:

LoadModule deflate_module modules/mod_deflate.so

一些老版本的浏览器在压缩特定类型的数据时会出现问题。定位它们很容易,这样做不会影响性能。Netscape 4.x 除了压缩 HTML 文件之外,还存在其他问题,因此我们应该添加以下内容:

BrowserMatch ^Mozilla/4 gzip-only-text/html

Netscape 4 的特定版本甚至有更糟糕的问题,所以我们可以完全禁用 gzip 压缩:

BrowserMatch ^Mozilla/4\.0[678] no-gzip

虽然 IE 有一个用户代理,也是以“Mozilla/4”开头的,但它对 gzip 压缩没有问题,所以我们可以取消那个浏览器的那些命令: 12

BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html


12 Apache 2.0.48 在 mod_setenvif 中有一个 bug,这就是为什么正则表达式在这个例子中看起来有点怪异。

我们不想压缩图像,因为它们已经有了自己的压缩方式。压缩这些文件只会给服务器和浏览器带来不必要的更大处理负载,而不会带来文件大小的优势。我们可以用这一行来停止:

SetEnvIfNoCase Request_URI \ \.(?:gif|jpe?g|png)$ no-gzip dont-vary

某些版本的 Adobe Flash 在通过 gzip 压缩时存在问题,因此,如果您发现 SWF 文件存在问题,可以将该行修改如下:

SetEnvIfNoCase Request_URI \ \.(?:gif|jpe?g|png|swf)$ no-gzip dont-vary

最后,一些代理和缓存服务器自己决定压缩什么和不压缩什么,并且可能通过这种配置提供错误的内容。下面一行告诉这些服务器执行我们设置的规则:

Header append Vary User-Agent env=!dont-vary

现在,任何对 CSS 文件(以及其他文件)的请求都将被支持它的浏览器压缩。

微软 IIS(互联网信息服务)

在 IIE 启用 gzip 甚至更容易。

对于 IIS 6(参见图 8-1 ),右键单击网站(或只是您想要启用压缩的网站)并选择属性。单击服务选项卡。您可以选择对所有静态文件(图像、CSS、字体、JavaScript、swf 等)和/或所有应用程序(动态)文件启用压缩。

images

图 8-1。 在 IIS 6 中启用 HTTP 压缩

对于 IIS 7,默认情况下对静态文件启用 gzip 压缩。对于动态(应用程序)文件,您可以使用以下命令启用它:

appcmd set config -section:urlCompression /doDynamicCompression:true

对于 IIS 之前的版本,文件压缩是不可靠的,我们不建议在生产环境中启用它。对于版本 6,您可以使用以下两个命令指定要压缩的静态文件类型:

cscript adsutil.vbs SET W3SVC/Filters/Compression/Deflate/HcFileExtensions "htm html txt css" cscript adsutil.vbs SET W3SVC/Filters/Compression/gzip/HcFileExtensions "htm html txt css"

最后一个参数(用引号括起来)在一个空格分隔的列表中列出了要压缩的静态文件。

对于 IIS 7,有一个名为 applicationHost.config 的文件使用 XML 语法,该文件通常位于 C:\ Windows \ System32 \ inetsrv \ config \ application host . config 中,该文件列出了应该压缩的动态和静态文件的 mime 类型。静态文件在一个名为<staticTypes>的标签中。以下是该标签的一些示例内容:

<staticTypes>     <add mimeType="text/*" enabled="true" />     <add mimeType="message/*" enabled="true" />     <add mimeType="application/javascript" enabled="true" />     <add mimeType="*/*" enabled="false" />   </staticTypes>

这个标签支持 mime 类型中的通配符,所以条目“text/*”将告诉 IIS 7 压缩 css(对于 CSS,mime 类型是“text/css”)。

几乎所有的现代网络服务器都支持 gzip 压缩。如果默认情况下未启用,请查阅 web 服务器的文档以了解如何启用它。

使用像 Firebug 这样的检查工具(或者许多其他工具中的一个,参见第十章),您可以检查响应和请求的发生,以确保 gzip 压缩正在发生,并了解您的文件压缩得如何。

下面是一个发生在特定站点的请求/响应过程的例子,在这个实例中:[stackoverflow.com](http://stackoverflow.com)

首先是请求(参见图 8-2)。

images

图 8-2。请求标题

然后是响应(见图 8-3 )。

images

图 8-3。响应头

从这些可以看出,我们最初的 Accept-Encoding 头声明我们支持 gzip(以及 deflate)。然后可以看到响应是用 gzip 编码的(通过检查 Content-Encoding 头)。这证明 gzip 在服务器上工作。您也可以查看该部分上方的行,以便对请求的整体结果一目了然(参见图 8-4 )。

images

图 8-4。 基本 HTTP 请求详情

我们可以从中看到请求的确切文件、从服务器返回的 HTTP 状态代码、请求文件的域、文件的大小以及加载文件所花费的时间(分解成更小的部分)。我们将在第十章中更详细地讨论 Firebug。

使用雅虎的另一个火狐插件!名为 YSlow,还可以看到文件被 gzipped 前的大小(见图 8-5):13

images

图 8-5。显示压缩前后文件大小的 YSlow】

这向我们展示了文件在被 gzipped 之前是 31.7 KB,之后是 7.8 KB。这大约节省了 75 %!显而易见,这种技术对于降低文件大小来说是非常重要且容易实现的。

YSlow 的一个更全面的替代产品是网页测试([www.webpagetest.org/](http://www.webpagetest.org/)),它是由 AOL 在 2008 年开源之前开发的。它允许你专门针对 IE 的所有版本运行测试,甚至可以运行多次并显示平均值,以获得更准确的结果。

内容分发网络(cdn)和域

浏览器对它们可以同时拥有的连接数进行了限制(通常在 35 左右)。这是一件明智的事情;如果我们试图同时请求 200 个文件,那么每个连接的速度都会急剧下降。不仅如此,它们还对特定域的同时连接数进行了限制。最近这个限制有所松动,有些浏览器让你修改这个值,但是默认: 14

  • IE 6 和 ie7 支持每个域 2 个同时连接。
  • IE 8 支持每个域 6 个同时连接。
  • IE 9 支持每个域 2 个同时连接(在撰写本文时——推测当 IE 9 退出测试版时,这个限制会增加)。
  • Firefox 2 支持每个域 2 个同时连接。
  • Firefox 3 支持每个域 6 个同时连接。
  • Firefox 4 支持每个域 6 个同时连接。
  • Safari 3、4 和 5 支持每个域 4 个同时连接。
  • Chrome 6、7、8 和 9 支持每个域 6 个同时连接。
  • Opera 9 支持每个域 4 个同时连接。
  • Opera 10 支持每个域 8 个同时连接。

YSlow 是一个非常有用的工具,可以准确定位是什么让你的网站变慢了,更重要的是,它给出了很多可以修复它们的提示。可以在[developer.yahoo.com/yslow/](http://developer.yahoo.com/yslow/)下载。

有趣的是,随着浏览器的更新,连接不仅在增加,还在上下波动。

大多数当前的浏览器支持每个域至少 4 个同时连接。这意味着,如果我们有一个 HTML 文件、一个 CSS 文件、一个 JavaScript 文件、4 个背景图像和 4 个内嵌图像,我们已经有 11 个需要连接的单独项目,其中最多有 9 个可以排在其他连接之后。

images 提示: Browserscope ( [www.browserscope.org/](http://www.browserscope.org/))是比较这些浏览器功能的一个极好的资源。

此外,当发送文件请求时,我们到目前为止显示的标题并不是随请求一起发送的唯一标题。另一个值得注意的头是“Cookie”头。Cookies 是用于在用户机器上存储持久数据和会话信息的信息片段,因此当从一页浏览到另一页时,数据可以保存在本地并被服务器引用。可以在 cookie 上设置过期日期,因此,当浏览器关闭并在以后重新打开时,过期日期仍然有效。cookie 与一个域相关联,任何来自该域的资源请求都会在请求中包含 cookie。再读一遍——“任何要求。”当我们从服务器请求动态内容时,cookie 对于这些页面的正确运行至关重要,但是 cookie 也会随着对静态文件(如图像或 CSS 文件)的请求一起发送。

在某些网站上,这些 cookies 的大小可能不小。单个 cookie 的最大大小为 4096 字节(4 KB),针对特定的域可以设置最大大小为 20 字节,因此对于给定的域,cookie 的总大小可能为 80 KB,不包括发送它们所需的分隔符。尽管这是一个极端的情况,但是如果我们在一个页面的每个图像请求中发送 80 KB 的数据,那么下载所有这些图像的时间将会大大增加。

解决这些问题的方法很简单。如果我们将主要/动态内容和静态内容领域分开,会发生两件大事:

  • 我们的 cookies 不再发送到我们的静态内容。这导致一个更小的请求,这是一个好消息,没有任何影响,因为数据是没有价值的。
  • 我们的连接限制适用于单个域,而不是一个域,使我们的连接限制增加了一倍。

实现这一点并不意味着对我们的架构进行大规模的反思。如果我们有一个初始域名[somedomain.com](http://somedomain.com),我们可以继续从[somedomain.com](http://somedomain.com)[www.somedomain.com](http://www.somedomain.com)提供我们的主页和动态内容。然后,我们可以创建[assets.somedomain.com](http://assets.somedomain.com),并将其指向带有域名服务器(DNS)条目的相同位置。我们的文件夹结构不需要做任何改变,我们已经提高了我们的性能。这使我们的连接限制增加了一倍,但是我们可以做得更好。我们可以遵循一个简单的规则,让我们在这些子域中保持一致,但仍然有很多子域。如果我们考虑我们引用的文件类型的文件后缀,我们可以在我们的域名中使用它们。例如:

  • [www.somedomain.com](http://www.somedomain.com)[somedomain.com](http://somedomain.com)为我们的主要和动态内容
  • [gif.somedomain.com](http://gif.somedomain.com)为 gif 图片
  • [jpg.somedomain.com](http://jpg.somedomain.com)对于 jpeg 图像
  • [png.somedomain.com](http://png.somedomain.com)对于 png 图像
  • [swf.somedomain.com](http://swf.somedomain.com)对于 swf 文件
  • [css.somedomain.com](http://css.somedomain.com)对于 css 文件
  • [js.somedomain.com](http://js.somedomain.com)对于 js 文件

如果我们始终如一地实现这一点,我们的 CSS 最终会有如下规则:

input {background: url(http://css.somedomain.com/css/img/shadow-background.gif);}

这完全违背了我们最小化文件大小的努力。相反,我们应该将域http://css.somedomain.com直接指向我们的 CSS 文件夹,并继续相对地引用这个文件夹中的文件。这是最小化 CSS 文件大小和拥有多个域之间的一个很好的折衷。我们可以引用我们的 CSS:

<link rel="stylesheet" href="http://css.somedomain.com/style.css" type="text/css" />

并继续像这样引用我们的图像:

input {background: url(img/shadow-background.gif);}

虽然上面的例子可能有些夸张,而且实现这么多子域也不可行,但仅仅一个子域就能带来值得的性能和带宽成本的改善。事实上,拥有多个域会对性能产生影响。每次额外的 DNS 查找都会导致性能下降,因此必须权衡这些好处和 DNS 查找成本。最多四个额外的域被认为是合理的数量。

使用子域的替代方法是使用内容分发网络(CDN)。

正如你将在本章后面看到的,在用户的计算机和他们连接的服务器之间有许多点;每一个都有性能影响。

CDN 将您的静态内容分布在许多服务器上,并总是试图从最接近用户位置的 CDN 响应用户。这可以极大地提高性能。实现 CDN 的困难在不同的选择中有很大的不同,但是使用它们的好处是不可否认的。虽然有几个免费的 cdn 可用,但对于高流量的网站,你需要使用商业产品来处理你所关心的那种带宽。一些比较知名的商业 CDN 产品如下:

  • 赤眉——www.akamai.com/
  • 【亚马逊云锋】——http://aws.amazon.com/cloudfront/
  • 微软 Windows Azure——www.microsoft.com/windowsazure/

对于一个高性能的网站来说,无论你是选择在本地用子域还是通过 CDN 来提供你的静态 CSS 文件,这些都是必须考虑的事情。

拥有更少的请求比文件大小更重要

对于一个规模合理的网站来说,你处理的不仅仅是一个 CSS 文件。以这种方式工作是荒谬的,原因有几个:

  • 多个开发人员可能会同时处理同一个文件,伴随着这种方法的所有负面问题,例如冲突和合并问题。
  • 当你只关心其中的一小部分时,扫描和阅读这种大小的文件是很困难的。
  • 只为一个网站提供一个文件不利于用户。在主页上下载一个大文件会使页面呈现非常缓慢,因为除了站点的特定区域之外,可能不需要许多规则或选择器。

更有意义的做法是将 CSS 分割开来,分别处理各个小部分。为了举例,让我们列出几个我们可能会用到的文件:

  • reset . css-重设. CSS
  • global.css
  • home.css
  • log in . CSS-登入

reset.css 和 global.css 文件是我们希望在每个页面上使用的文件,而 home.css 只用于主页,login.css 只用于登录页面。如果我们像这样保存文件,链接到 CSS 文件的主页代码将如下所示:

`

`

这有几个含义。最明显的是我们的主页 HTML 比它需要的要大一点。接下来,我们必须创建三个单独的连接来获取每个文件。除了最大连接限制,这对性能还有另一个更极端的影响。

事实证明,文件大小和连接限制并不是导致文件加载缓慢的唯一原因。让我们再来看看 Firebug 中向我们展示文件下载时间的部分(见图 8-6 )。

images

图 8-6。 一个 HTTP 请求/响应的阶段

您可以在这里看到,数据的接收并不是这个特定项的加载时间的最大原因。让我们把它分成几部分,就像我们在处理[css.somedomain.com/home.css](http://css.somedomain.com/home.css)一样。

域名服务器(DNS)查找

DNS 是一种将域名映射到 IP 地址的目录。在这一点上,浏览器首先试图找出css.somedomain.com指向哪里,因此它联系一个 DNS 服务器(特定于用户的网络设置),向该服务器请求该域的 IP 地址,并接收一个带有答案的消息。如果服务器已经知道结果,它会立即从缓存中返回结果;否则,它会询问更上游的其他 DNS 服务器。nslookup 工具(见图 8-7 )让我们很容易看到这个过程的结果。 15

images

图 8-7。nslookup 工具


15 每个 DNS 条目都有一个生存时间(TTL)。这是 DNS 服务器应该缓存结果的时间。它通常被设置为一天或更长时间,这就是为什么对 DNS 条目的更改可能需要一段时间才能传播到所有服务器。

幸运的是,DNS 是非常可缓存的,通常非常快。一旦用户的浏览器知道了特定域的 IP 地址,它(通常)在短期内不会再询问。但是,第一次从服务器获得响应确实会导致性能下降。 16

连接

此时,浏览器实际上正在与服务器建立连接。因为它有直接的 IP 地址,所以有理由假设这个过程应该非常快,并且浏览器可以直接连接到远程机器。不幸的是,事实并非如此。在用户机器和服务器之间有几个路由器,数据包需要通过它们来建立连接。traceroute 命令让我们看到了这一点的证据(见图 8-8 )。

images

图 8-8。traceroute 命令

正如您所看到的,在这个特定的例子中,在用户的计算机和服务器之间有 11 个独立的路由器。这将导致显著的性能下降,并且对服务器的每个请求都会发生这种情况。

发送

此时,连接已经建立,用户的浏览器正在向服务器发送数据。这些数据包括请求头和为该特定域本地存储的任何 cookies。对于 CSS 文件,我们已经讨论了如何最好地减少这种延迟。在前面显示的例子中,有很少的数据要发送,这实际上不会导致延迟。

等待

服务器现在已经收到了用户的请求并正在处理它。总会发生一定量的处理,即使它只是涉及远程服务器读取文件并将其返回,或者缓存服务器从内存中读取数据。如果请求的文件是动态的,并且需要特定于用户的处理(不能远程缓存),性能影响会更大。


一些浏览器,比如 Firefox,有一个他们引用的内部 DNS 缓存。通过这个缓存需要用户重新启动浏览器或使用扩展。

接收

最后,数据从服务器返回,浏览器接收它。在这种情况下,返回的数据非常小,网络速度非常快,因此延迟可以忽略不计。

从这个过程中我们可以看到,即使我们最大限度地减少了发送和接收的数据,仍然有其他因素在很大程度上影响性能。每次连接到服务器都会产生开销,而且这种开销通常比文件大小大幅增加所带来的影响更大。从中吸取的教训是,尽管最小化文件大小是最佳实践,当然也是值得努力的事情,但是请求越少对性能的影响就越大。 17

串联

下面显示的初始示例显然不是形成 CSS 文件的最佳方式:

`

`

让我们假设在这个实例中,我们的 reset.css 文件的大小是 2 KB,我们的 global.css 文件的大小是 4 KB,我们的 home.css 文件的大小是 6 KB。将这些文件连接成一个文件是减少服务器连接数量的一种明显的方法。由于 reset.css 和 global.css 在每个页面上都是必需的,因此我们应该将它们合并成一个页面,并将 home.css 分开,以防止用户不必要地下载重复的内容。实际上,对于这些小尺寸来说,这不是事实。如果我们将这三个文件连接成一个文件,我们将获得这个页面的最佳性能。但是,当用户浏览我们的登录页面时,我们希望包含这些文件:

`

****`

将这三个文件连接成一个文件似乎是违反直觉的——用户将像以前一样下载完全相同的 6 KB 文件(reset.css 和 global.css)。这似乎是重复劳动。但实际上,额外的 6 KB 下载不太可能像额外的 HTTP 请求的成本那样大(对大多数用户来说)。

这在高流量站点上如何工作取决于全局文件和特定文件的大小,并且需要测试以确保获得最佳结果。

我们还可以考虑将所有四个文件连接在一起。这是一个较大的初始点击,但是将在第一页之后被缓存,并且不会被服务器请求用于使用相同 CSS 的其他页面。

将这些文件连接在一起是一个简单的过程,但最好在构建过程中处理(如第九章中所述)。这使我们能够处理未压缩的单个文件,以获得最大的可读性,同时还能确保投入生产的代码尽可能的小和高效。


这就是为什么@import 指令应该少用的一个很好的理由;每一个额外的要求都会妨碍我们的表现。媒体选择器可以和 link 标签一起使用来引用单个文件,或者内嵌在 CSS 文件中,使我们的选择器更加具体。您应该使用哪一个取决于您有多少规则需要如此具体。HTTPS 连接招致更大的开销。

CSS spreads

CSS 精灵是提高性能的一个很好的方法,可以让页面感觉更快更灵敏。通常在我们的页面中,一个图形按钮可能有几种状态: 18

  • 正常—按钮的正常状态
  • 悬停(Hover)——显示的图像向用户表明他们的鼠标当前在可点击的东西上
  • 点击—显示的图像,向用户表明他们已经成功地与按钮交互
  • 禁用—显示的图像向用户表明该按钮不会对交互做出反应

为了达到这种效果,通常使用四个独立的图像。为了便于演示,我们来看看悬停状态:

`a {
        background: transparent url(img/button.gif) no-repeat 0 0;
        height:40px;
        width:120px;
        display:block;
}

a:hover {
        background: transparent url(img/button-hover.gif) no-repeat 0 0;
}`

浏览器选择如何处理悬停图像因浏览器而异。有些人可能会立即将其加载到缓存中,这样当用户悬停在锚点上时,加载图像就不会有延迟。其他人可能会选择等到用户悬停在元素上时再向服务器请求图像。第一个例子导致了对服务器的额外请求的性能成本。第二种方法会产生这种开销,但只是在加载之后,影响较小的时候。然而,在向用户显示该图像时存在延迟,给用户带来更差的体验。

这个问题是有解决办法的。CSS 精灵允许我们将多个图像堆叠成一个(见图 8-9 )。


不要依赖悬停状态来实现页面上的任何功能。这样做就是创建设备依赖关系;用户可能由于残疾而无法使用鼠标,或者可能正在使用对悬停状态没有概念的触摸屏界面。

images

图 8-9。 一个精灵形象的例子

这张图片包括我们的常规按钮,在它的正下方是我们的鼠标悬停状态。通过使用背景位置,我们可以在 CSS 中的两个地方引用同一个图像,但是有效地裁剪掉它的一部分,只显示我们想要的部分。使用这种技术,我们可以像这样修改我们的 CSS:

`a {
        background: transparent url(img/button.gif) no-repeat 0 0;
        height:40px;
        width:120px;
        display:block;
}

a:hover {
        background-position: 0 40px;
}`

现在只有一个 HTTP 请求,我们的按钮对鼠标悬停在它上面有即时反应。人们很容易对这项技术产生热情,并认为“嘿!让我们把整个网站上的每一张图片都放入一个巨大的 CSS 精灵中!”但是在你兴奋之前,关闭 Photoshop,看看为什么这是个坏主意:

  • CSS 精灵并不适合所有的事情。对于大小不可预测的元素(可能会随着浏览器中的font-size变化而调整大小或缩放),结果也可能是不可预测的。
  • 对于设计者和 CSS 开发人员来说,一个充满图像的大文件很快变得很难管理。
  • 决定图像中间的精灵需要改变尺寸会对图像的其余部分产生巨大的连锁效应。

相反,我们建议您在图像可能有多种状态的任何地方使用 CSS 精灵,例如前面演示的按钮。尽量避免允许使用精灵的元素以显示其他精灵或以不希望的方式裁剪它们的方式改变尺寸。如果这不可能,请确保在彼此之间添加足够的空间,这样,如果可视区域发生变化(例如当您增加字体大小或放大页面时),相邻的图像就不会显示出来。像往常一样,找到对用户和开发人员都有利的平衡点,并在组织内部决定如何实现这一点。一个例外是移动网站,那里的延迟可能很大,以至于每个额外的 HTTP 请求都会对页面呈现速度产生重大影响。

数据 URIs(统一资源指标)

除了将图像表示为一个单独的文件,还可以将图像编码成一大串数据,然后直接插入到文件中,如下所示:

<img src="data:image/png;base64,ABCDEFHIJKLMNOPQRSTUVWXYA" />

…或者:

background-image: url("data:image/png;base64,ABCDEFHIJKLMNOPQRSTUVWXYA");

这种方法有一个明显的好处:它为我们以这种方式包含的每个图像节省了额外的 HTTP 请求。在 CSS 精灵不合适的地方(例如,对于大小不可预测的元素),它们也能给你即时响应的悬停状态。不幸的是,它有更多的缺点:

  • 在比包含这些图像的文件更精细的级别上,对这些图像的缓存失去了控制
  • 该文件的表示实际上比常规图像文件大三分之一
  • IE 7 及以下版本根本不支持数据 URIs
  • IE 8 将数据 URIs 限制在 32 KB 或更少

由于这些限制,我们不建议使用数据 URIs,除非它们用于您可以准确预测或控制正在使用的浏览器的环境中。 19

缓存

缓存是存储经常访问的数据,以便可以更快地检索这些数据来提高性能的行为。缓存可能存在于几个不同的点:

  • 浏览器缓存—在客户端(浏览器)上,特定于机器的单个用户(尽管多个用户可以使用该帐户)
  • 【代理缓存(或共享缓存)—由互联网服务提供商(ISP)或第三方在客户端和服务器之间提供的缓存,由多个用户共享
  • 网关缓存—由 web 服务器(如缓存服务器或 cdn)的所有者实现的缓存,在许多用户之间共享

19Sveinbjorn Thordarson 在[www.sveinbjorn.org/dataurls_css](http://www.sveinbjorn.org/dataurls_css)提供了更多关于 URIs 数据的细节,以及生成它们的工具。

缓存主要服务于两个目的:减少延迟和减少网络流量。通常这两个目标符合每个人的最佳利益。每个人都希望用户尽快收到及时的信息和数据。每个人都希望传输尽可能少的数据,以进一步减少延迟并节省资金。作为一个高流量网站的所有者,我们唯一能够完全控制的缓存是我们已经实现的网关缓存。然而,我们可以给其他缓存一些命令和提示,并合理地期望它们会被遵循。这些命令是通过来自服务器的响应头来实现的,向浏览器和中间的代理缓存指示某个特定项在被再次请求之前是否应该在本地存储(以及存储多长时间)。

浏览器/代理缓存选择如何解释该信息最初是它的选择,并且不能保证我们使用的头将被正确实现,或者完全被读取,尽管大多数浏览器/代理现在已经标准化并且在它们如何处理这一点上相当一致。此外,浏览器中的用户设置可能会覆盖这种行为,在客户端完全禁用缓存的情况并不少见。缓存可以在短期内更有力地实现,这样,出现在多个地方的同一个图像就不需要再向服务器发出请求,并且单击后退按钮会立即得到响应。 二十

当提供内容时,由 web 服务器提供适当的缓存控制头。历史上控制缓存的第一种方法是 Expires HTTP 头,它可以设置为一个日期,在此日期之后,浏览器会向服务器重新请求数据。这种方法已被否决,原因如下:提供数据的资源(如网关缓存和代理缓存)之间的时间可能不同步,并且有必要以一定的时间间隔自动更新该值。相反,HTTP 1.1 规范定义了一个名为 Cache-Control 的新头,它存储一系列单独的值,让不同的缓存层做出智能决策,决定是提供文件的本地副本还是获取新的副本。这个决定基于两个因素: 21

  • 新鲜度—一个文件可以在一段时间内被标记为新鲜度,之后就是陈旧度,必须向服务器重新请求。
  • 验证—可以发出请求,让缓存机制知道本地存储的副本是否是最新的,而不必请求整个文件。

让我们快速看一下缓存机制通常是如何决定是否获取文件的新副本的。

  • 如果文件头指定不缓存该文件,或者该文件是通过安全(HTTPS)连接提供的,则不会缓存该文件。
  • 如果项目被认为是新的,它将被缓存,直到在这种情况停止后发出请求。
  • 如果一个项目不被认为是新的,将尝试验证它。如果发现它仍然有效,将继续提供缓存的项目。如果不是,将向服务器请求新的副本。

出于安全考虑,通过 HTTPS(安全 HTTP 连接)提供的内容不应该(通常也不会)缓存在本地或代理服务器上。

试图通过 HTML 中的 meta 标签来控制浏览器缓存是可能的,但很少有浏览器支持它们,因为浏览器的缓存机制会在 HTML 被解析之前检查数据并做出缓存决定。我们不推荐这种技术。

Cache-Control 头由几个逗号分隔的值组成。以下是一些更重要的价值观,其含义应该很好理解:

  • max-age—定义(以秒为单位)自请求以来文件应被视为新文件的时间段。
  • 必须重新验证—在特殊情况下,HTTP 允许缓存为过时的文件缓存提供服务。这个头告诉缓存机制不要遵循这种行为。
  • 不缓存—指示缓存机制从不缓存该文件。
  • no-store—指示缓存机制从不保留该文件的副本;缓存可以从内存中存储和提供,但不能写入磁盘。
  • public—明确表示共享缓存可以缓存某个项目,即使存在安全连接。
  • private—明确表示一个项目是可缓存的,即使有安全连接— ,但只在客户端缓存中
  • 代理-重新验证—在特殊情况下,HTTP 允许缓存为过时的文件缓存提供服务。这个头明确告诉共享缓存不要遵循这种行为。
  • s-maxage—定义(以秒为单位)自请求以来文件应被视为新鲜的时间段(但仅适用于共享缓存)。

下面是一个标题示例,它告诉所有代理永远不要缓存某个项目:

Cache-Control: no-cache, must-revalidate

以下是一个标题示例,它告诉所有代理将某个项目缓存一年,而不管该项目是否通过安全连接进行访问:

Cache-Control: max-age=31536000, public

验证器是代理用来检测一个项目是否已经改变的。在我们的第一个例子中,缓存机制将在每次请求时检查新版本。在第二种情况下,要等一年后才会检查。验证是向服务器发出特殊请求的行为,包括验证信息,如果缓存的副本仍然有效,服务器将不返回文件。缓存机制将使用两个主要的验证器。

第一个验证器是自文件最后一次修改以来的时间,它在名为 Last-Modified 的响应头中作为日期返回。代理可以发出一个名为 If-Modified-Since 的特定类型的请求,并在该请求中包含适当的日期,而不是发出两个请求(一个检查有效性,另一个检查本地副本是否无效)。

第二个验证器是在 HTTP 1.1 中引入的,叫做 ETag。ETag 是基于文件的内容构建的,是文件的一种指纹。当基于过时的缓存向服务器发出请求时,代理将发送一个名为 If-None-Match 的请求头,并包含存储的 ETag。 22

在这两种情况下,如果内容没有改变,则返回 304 HTTP 状态代码,这意味着没有修改,并指示缓存机制使用文件的存储表示。如果已更改,将返回包括文件内容在内的正常完整响应,并替换缓存的副本。

现代的 web 服务器会自动返回 Last-Modified 和 ETag 头,应该不需要进一步的配置。

images 注意:etag 的生成对于生成它们的文件来说是唯一的,对于提供它们的服务器来说也是唯一的。网关缓存或负载平衡服务器可能会使该报头不可预测。因此,它们通常不适合高流量的网站,应该使用 Last-Modified 标签。

使用浏览器时,有一些方法可以强制完全刷新并绕过缓存。例如,在 Firefox 中,按住 Windows 中的 shift 和 F5,或 OS X 中的 shift-command-R。这将发送一个名为 pragma 的额外请求头,其值为 no-cache,告诉服务器和任何共享缓存忽略请求中的任何缓存命令或缓存本地存储的数据,并从服务器返回最新版本。

我们应该缓存什么?

CSS 文件通常是非常静态的,因此非常容易缓存。这意味着它们的内容不会因其他因素而改变,如 cookies 或用户提供的内容。一个人收到的 CSS 文件内容与另一个人收到的内容完全相同。 23

在这种情况下,缓存是我们的朋友,可以显著提高各个级别的性能。因此,我们应该将图像、CSS 文件和其他静态文件的缓存设置为非常长期的。而且(通常)这些类型的文件没有安全考虑。这意味着不管这些文件是否通过安全连接被访问,我们仍然应该缓存它们,并向所有用户提供相同的副本。

在这种情况下,我们前面的长期缓存示例仍然适用:

Cache-Control: max-age=31536000, public

我们可以设置一个比一年更长的最大年龄,但是不太可能有人在一年后仍然在这个缓存中工作。

边缘缓存通常是一种有用且有效的技术,尤其是对于动态 CSS。我们将在下一章提到边缘缓存。


22 文件的指纹(或散列)(通常是 MD5 散列)使用算法来表示字符串。对照原始文件检查字符串可以让我们知道文件的内容是否已经更改。这通常用于确保您下载的文件是预期的文件并且没有损坏,但是在这种情况下,它也可以很好地检查文件是否已经更改。

动态创建的 CSS 文件不一定如此。在第九章中阅读更多相关内容。

应尽最大努力将所有 CSS 和 JavaScript 保存在外部文件中,而不是内嵌在页面中,因为 HTML 的缓存寿命较短,而 CSS 和 JavaScript 的缓存寿命较长。

版本控制

当处理最长时间的缓存文件时,版本控制成为一个真正的问题。当我们修改 CSS 文件时,缓存机制完全按照我们的要求运行,这意味着当我们想要停止缓存文件时,我们需要一个新的策略。唯一真正的解决办法是改变文件名。考虑下面的场景。

我们的文件 home.html 已经过修改,对标记进行了实质性的修改。我们的主样式表 style.css 也进行了修改,以适应这些变化。我们给了样式表一个长的缓存过期时间,以确保在不必要的时候不会被请求。突然间,我们所有的用户都在抱怨 home.html 看起来很破!发生的情况是,对 home.html 的更改已经到达我们的用户,但对 style.css 的更改还没有到达,因为该文件仍然在他们的本地缓存中可用。

解决这个问题的最好方法是版本化我们的 CSS。对 CSS 文件进行版本控制比听起来要简单得多。我们通过简单地手动修改文件名来包含版本号或日期来做到这一点。

例如,style.css 变成了 style.1.0.css。数字 1 和 0 分别表示“主要”和“次要”修订。每当我们做一个小的修改,我们就增加次要编号。如果这是一个戏剧性的修改,我们增加主要数字,并将次要数字重置为 0。因此,在这个实例中,我们只对 CSS 做了一点小小的更改,文件名变成了 style.1.1.css。我们的新标记引用了这个文件。我们在 CSS 文件上有很长的缓存过期时间,但在 HTML 上没有。如果用户正在获取我们的标记的最新版本,他们将获得 style.1.1.css,它还没有缓存在浏览器中,因为它是一个新文件。如果用户有我们的标记的旧版本,他们从缓存中获得 style.1.0.css,或者如果由于某种原因缓存中的那部分被清除或损坏,则作为新的请求。我们可以很容易地将文件命名为 style-2010-12-31.css 或 style-strawberry . CSS;重要的是文件名是唯一的。我们也可以通过在 URL 中包含一个查询字符串来突破缓存,比如 style.css?v=1.1,这将强制刷新文件,但会阻止我们保留以前的版本,从而阻止我们的向后兼容性。

多个文件同时存在的好处是,它确保了我们的系统的完整性,无论哪个版本的 home.html 运行在我们网站访问者的浏览器上。如果他们由于某种原因拥有旧版本(可能是共享缓存有问题,或者某个版本仍然被缓存),则仍然会请求 CSS 的适当版本。如果他们有更新的,同样是真实的。

作为我们构建过程的一部分(见第九章),我们可以让它自动化。

离线存储呢?

HTML5 为我们提供了一种离线存储机制,这样我们就可以在不向服务器发出任何请求的情况下提供页面和 web 应用程序。这可以通过html标签中的manifest属性来实现:

`

…`

清单文件(应该用我们指定的 mimetype text/cache-manifest)返回)包含关于哪些文件应该被自动缓存的信息。只有当缓存清单文件根据我们的正常缓存规则(即通过 ETags 或 Last-Modified)发生变化时,才会更新这些文件。清单文件示例如下:

CACHE MANIFEST /css/style.css /css/img/background.png /js/main.js

因为这个文件实际上覆盖了所有现有的缓存机制,所以当文件改变时,这个文件也需要改变。缓存清单中以散列符号开头的任何一行都被视为注释,这是注册对文件的更改的最简单方法,而不必修改清单本身:

`CACHE MANIFEST

Updated 2010/04/01

/css/style.css
/css/img/background.png
/js/main.js`

任何时候该文件被更改,所有声明的文件将被重新下载,您现有的缓存策略将不会生效。如果只有一个文件发生了更改(这种情况很常见),这是一种非常低效的管理缓存和性能的方式。由于这一点以及在撰写本文时浏览器支持较差,我们不推荐将它作为缓存常规内容的策略,尽管就其最初的用途而言,它仍然是一种很好的技术。

缓存清单在各种场景中都很有用,并且具有比这里列出的更复杂的语法,尽管它们超出了本书的范围。

渲染和解析

理解浏览器如何呈现我们的页面对于理解如何从您的页面获得最佳性能是至关重要的。浏览器首先读取 HTML,然后从上到下请求 HTML 中的项目。如果浏览器发现链接在页面底部的 CSS 文件,许多浏览器将什么也不呈现,直到页面中的所有内容都被加载,然后是 CSS 文件。在head标签中定位所有的link标签将确保这些标签首先被加载,然后浏览器可以逐步呈现页面。

每当浏览器找到@import 规则时,它会导入引用的文件,并在该指令之后的任何内容之前包含内容。在一些浏览器中(特别是 IE ),这阻止了文件的其余部分被读取和任何其他 CSS 文件被下载。这意味着浏览器必须等待该文件,然后才能继续下载页面的其余资源。这只是避免@import 规则的众多原因之一。

因为 JavaScript 文件是以线性方式读取的,所以它们会阻止页面的其他部分加载,直到它们被执行。因此,将它们放在尽可能靠近页面底部的位置,可以确保它们不会阻止我们并行下载和阅读任何其他文件。

当浏览器解析我们的 CSS 时,它从右向左读取我们的选择器,这可能非常不直观。尽管 id 非常有效,但是如果我们在它们后面加上其他不太具体的选择器,比如标记名,这些选择器将首先被读取。一旦浏览器找不到一个匹配的查询,它就会忽略选择器的其余部分,转到下一个规则。最快和最有效的选择器是那些 id 在选择器右边的选择器,id 非常具体,这通常意味着选择器的其余部分是不必要的。这将表明最有效的 CSS 可能是完全基于 id 的,但是用 id 填充我们的标记将(可能)是不明智的,并且很难管理。一如既往,必须在可管理和高效的代码之间找到平衡。

假设一个 ID 或类还包括一个标记名,如下所示:

div#mainContent {…} img.heroImg {…}

浏览器首先查询文档中具有这些 id 或类的所有元素,然后尝试进一步查询结果集中的标记。这很少是必要的,应该尽可能避免。

通用选择器(*)是您可以使用的效率最低的选择器:

body * {…}

尽管选择器看起来应该是一个简单的查询,但是它首先返回所有的元素,然后检查规则的下一部分(在本例中,元素应该在body标记内),这是一个非常低效的定位节点的方法。你应该尽可能避免它。您可以单独使用通用选择器,如下所示:

* {…}

有些人注意到在这种情况下,性能影响降低了,但是没有办法进行纯粹的测试。这将是一个“量子测试”,即观察行为会影响结果。除非必要,否则我们建议避免使用通用选择器。

CSS3 选择器更复杂,使用更多的资源来查询。因此,如果没有必要,您应该避免使用它们。id 总是 CSS 定位元素的最快方式,其次是类名。

CSS 文件的加载本身会导致一些浏览器阻塞,特别是 Internet Explorer。正如我们之前提到的,在页面加载了 JavaScript 之后,您可能会考虑“延迟加载”打印样式表。

然而,实事求是地说,CSS 性能很少会成为网站性能的瓶颈。你应该总是努力遵循最佳实践并保持 CSS 的高性能,但如果是以易读性或文件大小为代价的话就不要这样,因为这些更重要。

通过 JavaScript 改变属性

经常需要在与页面交互时修改样式,无论是简单的菜单可见性切换还是更复杂的事情。像下面这样的行很常见:

$("#elementID").css({    "height": "40px",    "width": "40px", });

在本例中,我们找到了 ID 为“elementID”的元素,并将其高度和宽度分别设置为 40 个像素。因为无法同时设置各个属性,所以这与下面的代码相同:

$("#elementID").css("height", "40px"); $("#elementID").css("width", "40px");

需要注意的是,以这种方式设置属性会强制在浏览器中重新绘制页面,这在性能方面代价很高。一切的定位都要重新计算两遍。如果我们设置更多的属性,它会发生更多次。相反,我们应该将它添加到我们的 CSS 中:

#elementID.modified {    height: 40px;    width: 40px; }

然后将我们的 JavaScript 更改为:

$("#elementID").addClass("modified");

现在,浏览器添加了该类,并且能够同时修改这两个属性,并且只强制进行一次刷新。虽然看起来我们在多个地方维护这些信息,但实际上 JavaScript 现在负责控制与元素相关的类,所有表示代码都保存在 CSS 文件中,这是应该的。如果我们必须在 JavaScript 的多个地方进行这种更改,那么将它抽象成一个简单的类显然是一种好的做法。

动画

动画是我们的页面将会做的最耗费资源的事情之一,可以通过很多方式实现(包括通过 CSS)。虽然我们不会深入 CSS 转换和过渡的细节,或者如何在页面上制作动画元素,但是我们会指出一些对性能有影响的事情。浏览器如何呈现页面对于理解如何获得最佳性能至关重要。

当通过 JavaScript 制作动画时,我们希望代码尽可能高效。除非我们在动画开始和结束之间的每一步都有一个类,否则我们不能一次轻易改变多个属性。为了用 JavaScript 制作动画,我们以特定的间隔改变 CSS 属性一定的量,直到我们达到目标值。例如,如果我们想让一个框从左向右移动,我们可以将“left”属性从 10px 修改为 100px,每 40 毫秒增加 10 个像素。这里有一系列可以遵循的好规则:

  • 动画尽可能少的属性。
  • 每次迭代做尽可能少的工作。
  • 将间隔时间设置为产生良好结果的最高值。将其设置得太低会导致性能不佳;设置太高会导致动画不稳定。

虽然通过 CSS 制作动画在撰写本文时仅在 WebKit 中得到真正的支持,但以这种方式制作动画带来了巨大的优势——最大的优势是这些动画是硬件加速的。可以编写代码,让 feature 检测 WebKit 动画事件,如果它们可用,就使用它们,如果它们不可用,就退回到 JavaScript。当通过 CSS 制作动画时,如果制作颜色动画,请注意在动画过程中浏览器会将所有内容转换为 RGBA。如果首先以这种格式提供颜色,您会看到更好的性能。

硬件加速

使用 CSS 过渡和转换将在支持它们的浏览器中使用硬件加速。通过将元素移动到 3D 空间,您可以欺骗浏览器对元素(及其子元素)强制进行硬件加速,如下所示:

… {    -webkit-transform-style: preserve-3d; }

虽然在某些情况下,这确实会提高性能,即使是在 2D 平面上,但在其他情况下,这可能会导致问题。移动设备不具备笔记本电脑和台式机的处理能力,不能很好地应对这种技术。此外,3D 平面中的元素通过不同的管道处理到其他元素,您可能会看到不同的文本渲染或抗锯齿结果。如果你能控制你的用户环境,这可能是一件有用的事情,但这不是我们推荐给典型网站的技术。

总结

这一章的目的是教你许多影响页面性能的因素,并向你展示一些你可以应用到你自己的 CSS 中以获得显著性能提升的最佳实践。在许多情况下,一种尺寸并不适合所有人,测试将是必要的,以了解您需要在这些不同的技术之间找到的平衡。这一支出将会带来回报,既体现在网站访问者的体验方面(因此也体现在他们对你的网站和组织的好感方面),也体现在你的带宽成本方面。

下一章关注的是动态 CSS,将教你如何为不同的用户提供不同的 CSS,以及如何使用 LESS 和 Sass 这样的预处理程序来使编写 CSS 更快、更实用、更有趣。

九、动态 CSS

到目前为止,这本书一直认为 CSS 文件是静态资产。也就是说,在每个实例中,从服务器请求并传递给站点访问者的内容是完全相同的。尽管这几乎总是实现 CSS 的预期行为和最高效的方式,但是可以根据其他因素定制 CSS 的输出,并向不同的用户交付不同的 CSS。

您可能想这样做有几个原因,有些是为了开发人员的利益,有些是为了用户的利益。从开发人员的角度来看,使用变量 1 或者动态填充 CSS 是很有用的。对于任何重复的代码,例如颜色或 CSS 的特定块,存储一次并在一个地方维护它会使代码更严格、更严格,修改起来也更容易、更安全。无论出于什么原因,在 CMS 驱动 CSS 的情况下,连接到服务器端组件或数据库可能是填充 CSS 或构建/输出选择器所必需的。有些工具使用动态行为来改进语言,使开发更有成效。

从用户的角度来看,一些网站选择为用户提供定制呈现给他们的 CSS 的能力,比如多种配色方案或版式。这可能是出于纯粹的美学原因,或者是为了解决诸如视力不佳或色盲等无障碍问题。

在本章中,您将了解以下内容:

  • CSS 扩展和预处理程序
  • 评估第三方预处理器
  • 用服务器端技术服务 CSS
  • 连续累计
  • 缓存注意事项

CSS 扩展和预处理程序

有几个项目旨在构建 CSS,并提供一些许多开发人员认为缺失的功能。变量、注释样式和快捷方式是解决这些问题的常见方法。为了使用这些特性,开发人员通常创建一个具有不同文件后缀的文件,并以新的语法在该文件中工作;然后使用一个编译器来读取这个文件并输出一个正常的 CSS 文件。在这个领域有两个大的竞争者,LESS 和 Sass,尽管它们处理许多相同的问题,但它们之间有重要的差异。


关于在规范中包含 CSS 变量的讨论正在进行中,并且已经进行了一段时间。Tab Atkins Jr 最新(个人)稿可在[www.xanthir.com/blog/b4AD0](http://www.xanthir.com/blog/b4AD0)阅读。

少了

LESS 是 Alexis Sellie 创建的预处理器。它使用类似 CSS 的语法,并对 CSS 进行了许多改进。我们在版本 1.0.41 上执行了我们的测试。它是内建的 JavaScript,所以你可以包含一个 LESS 文件,就像它是常规的 CSS 一样(尽管有不同的rel属性),和 less.js(来自http://lesscss.org),它将在页面加载时被编译。

`

`

然而,使用 less.js 会引入对 CSS 的 JavaScript 依赖,所以我们建议使用命令行编译器。LESS 的最新版本可用于 node.js(用于创建网络应用程序的服务器端 JavaScript 框架)。从[nodejs.org](http://nodejs.org)获取 node.js。一旦你安装了它,获得更少的包的最简单的方法就是使用节点包管理器。关于安装的说明可在[howtonode.org/introduction-to-npm](http://howtonode.org/introduction-to-npm)获得。最后,您可以运行这个命令来安装 LESS 包:

npm install less

结果如图 9-1 所示。

images

图 9-1。安装少节点包

辛苦到此结束。??它安装好了。这个安装为您提供了一个主要工具:lessc—LESS 编译器。使用这个很简单。您只需运行这个文件,将您希望它编译的 LESS 文件的路径传递给它,LESS 将创建一个与源文件同名的 css 文件,但带有“CSS”后缀。默认情况下,lessc 将输出到 stdout——直接输出到终端(见图 9-2 )。

images

图 9-2。 lessc 输出到 stdout

要输出到文件,请将 lessc 指向该文件。

lessc style.less > compiled/style.css

您也可以通过传递–x 作为参数来使用内置缩小(参见图 9-3 )。

images

图 9-3。 lessc 缩小版


LESS 的最初版本是用 Ruby 构建的,安装起来简单得多——尤其是在已经包含 Ruby 的 OS X 上。其实 LESS 在本章初稿写完之后就正式转到 node.js 了!这表明 LESS 仍在积极开发中。

这种缩小没有 YUI 压缩器有效——它在开始时留下了尾部分号——所以我们建议配合使用另一种压缩器。

命令行工具是开始的一种方式,但是我们马上会看到其他更友好的方式。现在,让我们详细看看 LESS 提供给我们的额外功能。

变量

变量经常被认为是 CSS 最大的缺失特性之一,它可能非常强大和有用。您可以使用变量来存储您希望多次使用的任何信息,并在一个地方定义它。这意味着改变这个定义一次将影响我们使用该变量的每个实例。这是一种非常高效和稳健的工作方式。LESS 中的任何变量都以@符号为前缀。在 LESS 中声明变量非常简单,只需声明名称、冒号、值和行尾的分号。由于变量永远不会到达最终解析的 CSS,关于可以使用哪些字符的规则要简单得多;您正在为一个解析器而不是许多浏览器构建。

变量不能反复设置(它们不是变量! 3 )。事实上,LESS 中的变量是常量。一种常见的编程约定是,常量全部大写,下划线作为单词分隔符,这使得在 LESS 文件中直观地定位变量变得非常容易,但是您应该使用您和您的团队认为最合适的命名约定(许多人更喜欢使用类似 CSS 的格式)。下面是变量用法的一个基本例子:

`@PRIMARY_COLOR: #faa344;

content {color: @PRIMARY_COLOR;}`

编译后,它会发出以下内容:

#content {   color: #faa344; }

如您所见,LESS 对 CSS 的外观有自己的想法:多行缩进两个空格,规则之间没有垂直间隙。因为这个想法是您将主要在 LESS 中开发,所以输出的格式远没有那么重要。

你可以多次重用你的变量。LESS 也理解作用域的概念,这意味着你可以在不同的上下文中声明同一个变量,并且只为那个上下文改变值。顶层(任何选择器之外)的任何变量都属于全局范围。选择器中声明的任何变量都将该选择器作为其作用域。一个变量只存在于它的作用域内,并覆盖任何更大作用域的变量。当引用一个变量时,LESS 将在引用的局部范围内查找,然后使用它遇到的第一个值沿着链向上找到全局范围。用一个例子可能更容易理解。

这少了:

`@PRIMARY_COLOR: #faa344;

content {

@PRIMARY_COLOR: red;
   color: @PRIMARY_COLOR;
}

footer {

color: @PRIMARY_COLOR;
}`

…编译成以下内容:


3 和可扩展样式表语言(XSL)中一模一样,在 XSL 中变量不是变量,因此命名非常非常糟糕。

`#content {
  color: red;
}

footer {

color: #faa344;
}`

@PRIMARY_COLOR的第一个声明在任何选择器之外;它是全球性的。第二个声明是在#content的上下文中。此时,它会覆盖第一个声明,因为该声明的范围更广(不太具体)。第二个声明只存在于它的上下文中,所以一旦我们关闭了#content的大括号,这个变量就被丢弃了,取而代之的是我们的初始声明。当同一个变量在同一个作用域中被多次声明时,除了第一次声明之外的所有声明都被忽略。

通过用空格分隔,可以在一个属性中输出多个变量,如下所示:

`@VERTICAL_MARGIN: 10px;
@HORIZONTAL_MARGIN: 15px;

content {

margin: @VERTICAL_MARGIN @HORIZONTAL_MARGIN;
}`

它输出以下内容:

#content {   margin: 10px 15px; }

但是目前还没有将字符串连接在一起的方法——不留空格会减少为您添加的空格。为了证明:

`@VERTICAL_MARGIN: 10px;
@HORIZONTAL_MARGIN: 15px;

content {

margin: @VERTICAL_MARGIN@HORIZONTAL_MARGIN;
}`

在这段代码中,我们删除了输出变量之间的空格,但结果是一样的:

#content {   margin: 10px 15px; }

除了颜色,LESS 中的变量实际上可以表示我们在 CSS 中用作值或在值内使用的任何东西,从文本字符串到数字。

混合蛋白

一个 mixin 是一段很容易在另一个地方重用的代码。您可以将它视为类似于 JavaScript 中的函数或方法,或者从另一个 CSS 规则继承属性的一种方式。使用 mixins,可以在另一个 CSS 规则中使用一个 CSS 规则。通过在另一个规则中只使用前一个规则中的选择器,引用规则中的所有属性都将被包括在内。

这少了:

`.callToAction {
   color: #123;
}

content {

.callToAction;
}`

…产生这个 CSS:

`.callToAction {
  color: #123;
}

content {

color: #123;
}`

值得注意的是,原来的规则仍然存在于我们输出的 CSS 中。如果您只是希望这是一个不针对任何元素的可重用代码块,那么这不会像您希望的那样高效,并且会在输出中产生无关的代码。

您也可以以类似于函数或方法的方式使用 mixins。这意味着您可以将值传递到 mixin 中,也可以在没有传递任何值时使用默认值。少管这些叫参数混合。为了表明 mixin 接受参数,可以在它后面加上括号,括号内是变量的名称。如果有多个变量,则应该用逗号分隔,每个变量后面应该跟一个冒号和它们的默认值(如果适用)。例如,这少了:

`.box (@HEIGHT: 40px, @WIDTH: 40px) {
   height: @HEIGHT;
   width: @WIDTH;
}

content {

.box(20px,80px);
}
.callToAction {
   .box;
}`

…编译成这个 CSS:

#content {   height: 20px;   width: 80px; } .callToAction {   height: 40px;   width: 40px; }

正如您所看到的,这个方法有一个额外的好处,就是不再在我们的代码中输出 mixin!如果我们意识到这一点,我们可以使用这个技巧来使不需要参数的 mixins 不再出现在我们的输出中,只需在它们的名称后面加上空括号,如下所示:

`.callToAction () {
   color: #abc;
}

content {

.callToAction;
}`

这产生了这个 CSS:

#content {   color: #abc; }

这种方法不适用于比单个类名或 ID 更复杂的选择器,所以不能用标签名或派生选择器定义 mixin 这将导致错误。LESS 足够聪明,知道任何不包含属性(或只包含变量)的选择器都不会在编译后的 CSS 中输出。 4

实际上,不指定默认值会使这些参数成为“必需的”如果你引用一个没有设置默认值的 mixin,并且你没有传递值给它,这将导致一个错误。

Mixins 对于抽象出规范中尚未最终确定的实验属性特别有用。对于不同的浏览器,它们可能会有略微不同和复杂的语法,并使用供应商前缀来分隔它们。将这些属性保存在单独的模块中,可以使它们在规范和支持发生变化时易于修改,并将这些变化反映在整个代码中,同时最大限度地减少人为错误,并消除每次使用时写出每个特定于供应商的版本的需要。


4LESS 的早期版本允许您包含文档中存在的任何选择器,而不管选择器由多少个元素组成。

嵌套规则

可以将规则嵌套在另一个规则中,然后沿着选择器链向上构建 CSS 的最终选择器。再举一个例子:

#content {    .callToAction {       a {          color: #321;       }    } }

结果是:

#content .callToAction a {   color: #321; }

这既是一种祝福,也是一种诅咒。通过缩进和嵌套,很容易模仿 HTML 的结构,这是一种非常直观的工作方式。然而,产生的选择器可能冗长而具体,这不太可能是我们想要的 CSS 结果。这里有一个更复杂的例子:

#content {    .callToAction {       a {          color: #111;       }       p {          color: #111;       }       div {          color: #111;       }    } }

结果是:

`#content .callToAction a {
  color: #111;
}

content .callToAction p {

color: #111;
}

content .callToAction div {

color: #111;
}`

这个 CSS 不仅比它可能需要的更具体,而且还可以通过多个逗号分隔的选择器更有效地完成,如下所示:

#content .callToAction a, #content .callToAction p, #content .callToAction div {   color: #111; }

这种方法还鼓励您编写特定于某些元素的 CSS,而不是可重用的类,这是一种低效的 CSS 编写方式。除非您使用这种方法对混合或变量进行分组(稍后将在“名称空间”和“访问器”小节中介绍),或者您使用类名或 ID 作为名称空间,否则我们不建议以这种方式使用嵌套规则。

可以混合嵌套的规则和属性,如下所示:

#content {    position: absolute;    p {       color: red;       a {          color: blue;       }    } }

结果如下:

`#content {
  position: absolute;
}

content p {

color: red;
}

content p a {

color: blue;
}`

您还可以嵌套伪类,这是该特性的一个很好的用途。通过简单地包含冒号和伪类,您可以一次生成几行。未编译的 LESS:

a {    :hover {       color:blue;    }    :visited {       color:red;    }    :link {       color:green;    }    :active {       color:yellow;      } }

…以及由此产生的 CSS:

a :hover {   color: blue; } a :visited {   color: red; } a :link {   color: green; } a :active {   color: yellow; }

您还可以使用& combinator 来输出嵌套规则和属性中的整个选择器。这对于链接类名特别有用:

#content {    .callToAction {       color: red;       &.disabled {          color: gray;       }    } }

结果是:

`#content .callToAction {
  color: red;
}

content .callToAction.disabled {

color: gray;
}`

然而,如果你试图使用& combinator 将父选择器放在当前选择器之后,LESS 将重新排列这些选择器:

#content {    body & {       color:blue;    } }

它变成如下:

#content body {   color: blue; }

这几乎肯定不是您的意图(您会期望选择器是body #content)。如果您理解嵌套选择器的潜在负面影响,它们会非常有用。

操作

LESS 允许我们对值执行基本的数学运算,并在输出 CSS 之前对其求值。可用的运算有加、减、除和乘(没有模运算符)。这里有一个简单的例子:

`@BORDER_TOP: 1 + 1;
@BORDER_RIGHT: 1 - 1;
@BORDER_BOTTOM: 2 * 2;
@BORDER_LEFT: 6 / 2;

.callToAction {
   border-top:@BORDER_TOP;
   border-right:@BORDER_RIGHT;
   border-bottom:@BORDER_BOTTOM;
   border-left:@BORDER_LEFT;
}`

结果如下:

.callToAction {   border-top: 2;   border-right: 0;   border-bottom: 4;   border-left: 3; }

运算符(+或-)前后必须有一个空格,这一点很重要(其他空格字符会导致错误),否则在对变量执行运算时,结果可能有些不可预测。LESS 也可以将这些运算符应用于单位。等式中只需要出现一个单位,更少的会假设所有其他单位都是这个单位。尝试使用多个单位执行计算将会导致错误,并且无法编译。 5 这里有个例子:

`@BORDER_TOP: 1px + 1px;
@BORDER_RIGHT: 1 - 1px;
@BORDER_BOTTOM: 2em * 2;
@BORDER_LEFT: 6 * 20%;

.callToAction {
   border-top:@BORDER_TOP;
   border-right:@BORDER_RIGHT;
   border-bottom:@BORDER_BOTTOM;
   border-left:@BORDER_LEFT;
}`


5 这不同于 CSS3 中提议的calc()函数(由 Firefox 的最新测试版支持——使用–moz-cal()供应商前缀——并计划由 IE9 支持),它足够聪明地混合了单元,因为它是在页面呈现时计算的,而不是预先计算的。更多信息请点击这里:[www.w3.org/TR/css3-values/#calc](http://www.w3.org/TR/css3-values/#calc)

结果如下:

.callToAction {   border-top: 2px;   border-right: 0px;   border-bottom: 4em;   border-left: 120%; }

请注意,%被视为一个单位,并不充当模数运算符 6 ,也不用于计算其他值的百分比。另请注意,LESS 在使用其内置压缩时,即使值为零(在不必要的情况下),也会继续附加该单元,但附加的缩小脚本可以消除这种情况。

您不能对简单的字符串值执行操作,但是 LESS 足够聪明,可以对颜色(包括 HSL 和 HSLA 颜色)执行类似的计算,尽管它会将颜色转换为十六进制。实际上,当在计算中使用 RGBA 和 HSLA 颜色时,当前版本甚至将它们转换为十六进制,完全失去了 alpha 通道,所以我们建议不要这样做。作者承诺将在下一个版本中解决这个问题。对命名的颜色执行计算是不可能的,因为 LESS 会将其视为一个字符串,而不是一种颜色—事实上,使用加法运算符时,LESS 会在第一个颜色后附加一个空格,这很可能不是您想要的结果。在计算中使用颜色时,LESS 会将颜色分解为各个分量,根据每个分量计算颜色,然后重新附着它们。您甚至可以在计算中使用多种颜色,LESS 将独立评估红色、绿色和蓝色通道(或色调、饱和度和亮度)。也可以在操作中使用变量。这里再举一个例子:

`@BORDER_TOP_COLOR: #aabbcc / 2;
@BORDER_RIGHT_COLOR: @BORDER_TOP_COLOR + #111;
@BORDER_BOTTOM_COLOR: rgb(13,26,39) * 2;
@BORDER_LEFT_COLOR: rgba(10,20,15,0.1) + @BORDER_RIGHT_COLOR;

.callToAction {
   border-top-color:@BORDER_TOP_COLOR;
   border-right-color:@BORDER_RIGHT_COLOR;
   border-bottom-color:@BORDER_BOTTOM_COLOR;
   border-left-color:@BORDER_LEFT_COLOR;
}`

编译后的代码如下:

.callToAction {   border-top-color: #555e66;   border-right-color: #666f77;   border-bottom-color: #1a344e;   border-left-color: #708386; }


6 模运算符用于求除法运算后的余数。

如你所见,@BORDER_LEFT_COLOR的整个 alpha 通道已经丢失。在使用颜色操作时,您需要意识到这个障碍。

您可以使用这些方法来使颜色变亮或变暗,并使用单个主要变量来创建该颜色的阴影。这是一个非常强大的方法,只基于几个初始颜色来生成配色方案。

颜色功能

LESS 中新引入了一些用于处理颜色的内置函数:

  • lighten(color, amount)

    images lighten()允许您按百分比将颜色变浅。

  • darken(color, amount)

    images darken()允许您将颜色加深一个百分比。

  • saturate(color, amount)

    images saturate()允许你进一步按百分比饱和一种颜色。

  • desaturate(color, amount)

    images desaturate()允许您按百分比进一步降低颜色的饱和度。

  • spin(color, amount)

    images spin()允许您修改颜色的色调度数(正或负)。

  • fadein(color, amount)

    images fadein()允许您按百分比增加颜色的不透明度。省略%符号仍会导致参数被视为百分比,因此 0.1 与 0.1%相同。更少不会增加超过 100%的不透明度。

  • fadeout(color, amount)

    images fadeout()允许您按百分比降低颜色的不透明度。省略%符号仍会导致参数被视为百分比,因此 0.1 与 0.1%相同。“更小”不会将不透明度降低到 0 以上。

对于其中的每一个,LESS 会在应用变换之前将颜色转换到 HSL 颜色空间。然后,它会将颜色转换回十六进制结果,除非不透明度小于 100%,在这种情况下,它会将其转换为 RGBA。下面是这些颜色函数的使用示例:

`@COLOR: #aabbcc;
@COLOR1: lighten(@COLOR, 10%);
@COLOR2: darken(@COLOR, 10%);
@COLOR3: saturate(@COLOR, 10%);
@COLOR4: desaturate(@COLOR, 10%);
@COLOR5: spin(@COLOR, 10);
@COLOR6: spin(@COLOR, -10);
@COLOR7: fadeout(@COLOR, 50%);
@COLOR8: fadein(@COLOR7, 25%);

a {
   color: @COLOR;
   :link {
      color: @COLOR1;
   }
   :visited {
      color: @COLOR2;
   }
   :hover {
      color: @COLOR3;
   }
   :active {
      color: @COLOR4;
   }
   .callToAction {
      :link {
         color: @COLOR5;
      }
      :visited {
         color: @COLOR6;
      }
      :hover {
         color: @COLOR7;
      }
      :active {
         color: @COLOR8;
      }
   }
}`

结果是:

a {   color: #aabbcc; } a :link {   color: #cad5df; } a :visited {   color: #8aa2b9; } a :hover {   color: #a3bbd3; }   a :active {   color: #b1bbc5; } a.callToAction:link {   color: #aab5cc; } a.callToAction:visited {   color: #aac1cc; } a.callToAction:hover {   color: rgba(170, 187, 204, 0.5); } a.callToAction:active {   color: rgba(170, 187, 204, 0.75); }

这是一个很好的演示,说明了如何用一种颜色来生成一个完整的配色方案。还有提取单个 HSL 通道的内置函数,称为(可预测的)hue()saturation()lightness()。您可以使用这些函数基于其他颜色的通道来构建颜色。这些功能中的任何一个都可以与其他功能相结合。这里有一个例子:

`@COLOR1: #123;
@COLOR2: #456;
@COLOR3: #789;
@NEW_COLOR: lighten(hsl(hue(@COLOR1),saturation(@COLOR2),lightness(@COLOR3)), 10%);

a {
   color: @NEW_COLOR;
}`

使用我们的新颜色,结果如下:

a {   color: #8ea1b4; }

名称空间

在 LESS 中,可以在嵌套的 mixins(名称空间)中将属性组合在一起,并通过使用>字符将这些属性作为组进行引用。这对于封装和保持代码整洁非常有用。和往常一样,这是最容易用例子来证明的:

#box () {    .square {       width:80px;       height:80px;    } } .content {    #box > .square; }

…结果是:

.content {   width: 80px;   height: 80px; }

这种技术非常有用,尽管它在 LESS 的早期版本中有些缺陷,在早期版本中,您可以访问名称空间中的单个属性和变量。可以嵌套任意多层次的名称空间。

正在评论

LESS 还支持 C 风格、单行注释以及块注释,但是只有 CSS 风格的注释会在编译后的 CSS 中输出,除非您使用内置的缩小功能。这里有一个简单的例子:

`/*
A regular CSS block level comment:
*/

content {

color: black; //A single line C-style comment
}`

结果是:

`/*
A regular CSS block level comment:
*/

content {

color: black;
}`

导入

LESS 支持@import 指令直接将文件包含在其他文件中。您可以以这种方式包含 CSS 文件或更少的文件。当包含较少的文件时,文件后缀是不必要的。对于本例,我们需要演示几个文件的内容:

  • 风格. less:

`@import "style2";

content {

color: red;
}`

  • style2.less:

`footer {
   color: red;
}

@import "style3.css";`

  • style3.css:

header {    color: red; }

编译 style.less 现在会产生以下结果:

`@import "style3.css";
footer {
  color: red;
}

content {

color: red;
}`

images 注意:注意到什么了吗?LESS 足够聪明地意识到,当我们对 CSS 文件而不是 LESS 文件使用@import 时,我们并不打算让它组合这些文件,而让我们的代码保持原样。但更好的是,LESS 知道@import 命令需要在文件的顶部,并代表我们移动了它。LESS 的早期版本甚至聪明地意识到,当多个选择器具有相同的内容并且彼此直接相邻时,它应该用逗号分隔这些选择器并将它们分组在一起——遗憾的是,这种行为似乎已经消失了。

这种方法对于保持特征分离非常有用。在任何 LESS 文件中声明的任何变量或混合对其他人都是可用的,包括那个文件。这使得创建一个变量的配置文件变得很容易,例如,一个变量的配置文件用来驱动剩下的较少的代码。重要的是要记住,构建配置文件会产生一个空的 CSS 文件,因为其中没有标准的 CSS 代码。您必须始终编译包含其他文件的最底层文件,以构建正确的 CSS。这意味着编译该文件,即使它没有任何变化。

结论

使用附带的编译器只是一种方式,你可以创建 CSS 文件,从他们不太对口。如果使用 node.js,可以在 node.js 应用程序中直接使用节点包。 7 对于那些更喜欢图形用户界面(GUI)而不是命令行的人来说,一个名为 Less.app 的应用程序在[incident57.com/less/](http://incident57.com/less/)可供 OSX 使用,它具有监视文件夹或文件,并自动编译的能力(参见图 9-4 )。


7[lesscss.org/#-client-side-usage](http://lesscss.org/#-client-side-usage)阅读更多关于在 node.js 中使用 LESS 的信息。

images

图 9-4。 LESS.app 版本 2.3.1

还有流行的 ide、文本编辑器和 web 框架的插件,比如 Apache、Grails、PHP ( [leafo.net/lessphp/](http://leafo.net/lessphp/))和。NET ( [www.dotlesscss.org/](http://www.dotlesscss.org/))。使用这些插件可以减少在读取时(当用户试图访问文件时)为您编译的代码,因此您不需要手动编译,缓存可以避免性能问题。

不幸的是,少也有不好的一面。虽然简单易学,但很少有开发人员不太熟悉。你需要严格实现它,并教育你的开发人员:任何开发人员不小心直接修改 CSS 代码,都会在下次编译时丢失更改。例如,嵌套规则可能会使控制选择器的特异性变得困难。最糟糕的是,当试图在 Firebug 或任何其他 web 检查器中调试 CSS 时,您正在检查的文件(输出的文件)不会以任何直观的方式与您的源文件相关联(行号不匹配,选择器可能不相同,等等),除非您使用的是 LessPHP。

images 提示:如果你正在使用 LessPHP,一个名为 Fireless ( [addons.mozilla.org/af/firefox/addon/fireless-for-firebug/](https://addons.mozilla.org/af/firefox/addon/fireless-for-firebug/))的 Firebug 插件将使你能够调试你的 less 代码,只要你启用 LessPHP 的“debug_info”选项。

此外,代码和行为在不同版本之间发生了巨大的变化,没有变更日志或文档来解释发生了什么变化,或者如何修改现有代码来应对这些变化。如果您使用的是 LESS.app、LessPHP 或。此外,不同版本之间的接口和代码可能会有很大的不同,因为每个代码版本都需要更新端口,可能需要一段时间才能跟上。请阅读本章后面的“评估第三方预处理程序”,了解如何将这些评论应用于您在开发过程中使用的任何第三方代码。

也就是说,LESS 是编写 CSS 的一种有吸引力和有趣的方式。然而,我们提到的问题使我们很难推荐它用于生产系统;如果功能和实现变化如此之大,而没有记录的理由,并且错误悄悄进入系统,这表明没有回归测试发生——这不是你想在高流量网站上进行 beta 测试的那种事情。在官网http://lesscss.org/了解更多关于 LESS 的内容。

萨斯

Sass 代表语法上令人敬畏的样式表,就像首字母缩写词一样,它肯定是有前途的。Sass 比 LESS 存在的时间更长,已经是第三次修订了。因为 Sass 太成熟了,所以我们先介绍得少一点似乎有点不公平,尤其是现在它们的语法如此相似。然而,由于 LESS 与 CSS 和 mixins 的相似性,它的吸收速度更快。也就是说,Sass 的开发者们很快就开始效仿,而且做得优雅而恭敬。

Sass 的原始语法以 Haml 风格缩进。 8 在这个语法中,空格,比如缩进和回车,很重要,暗示着血统和结构。尽管 Python 开发人员喜欢这一点(Python 也将空白视为其语法的一部分),但许多其他开发人员习惯于随意使用空白来格式化他们的文档。我们将巧妙地回避这一讨论,而是说从 SASS 3 开始,语法已经被称为 Sassy CSS (SCSS)的东西所取代。

SCSS 与 LESS 的相似之处在于,它的结构与 CSS 完全一样,并且每个有效的 CSS 文件也将是有效的 SCSS 文件。从视觉上看,旧语法(.sass文件)和新语法(.scss文件)的区别在于,新语法有大括号,而旧语法只有缩进;新的以分号结束规则,而旧的用回车。从功能上来说,使用 Sass 与使用 LESS 非常相似,但是 Sass 的文档要全面得多,而且有许多函数是 LESS 中没有的。

Sass 和 LESS 的原始版本一样,是用 Ruby 构建的,所以要安装它,首先需要安装 Ruby。如果使用的是 Windows,可以从[rubyinstaller.org/downloads/](http://rubyinstaller.org/downloads/)获取安装程序;如果您有 OS X,它已经安装。然后,要安装 Haml(包括 Sass),在命令行输入(见图 9-5 ):

gem install haml


Haml 是一种简洁的 HTML 模板语言。在[haml-lang.com/](http://haml-lang.com/)阅读更多关于 Haml 的信息。从 3.1 版本开始,Sass 将与 Haml 分开提供。

images

图 9-5。 安装 Haml(包含 Sass)

你完蛋了。您可能需要将 Ruby 添加到您的 PATH 变量中——如果这是真的,那么当您运行上面的命令时,就会得到警告。将文件编译成 CSS 的 Sass 命令叫做sass。同样,您可以在单个文件上使用它:

sass style.scss style.css

如果您未能提供输出文件作为参数,结果将简单地输出到标准输出(见图 9-6 )。

images

图 9-6。 Sass 输出到 stdout

你可以观察文件的变化来自动生成编译后的 CSS,语法略有不同:

sass --watch style.scss:style.css

或者,您可以观察整个文件夹,并将该文件夹中的任何更改输出到另一个文件夹,如下所示:

sass --watch path/sass:path/css

在这种情况下,新文件以与原始文件相同的名称命名,但后缀更改为. css。

总的来说,Sass 提供了更多的功能。LESS 的文档简单而缺乏,而 Sass 的文档却很全面且写得很好。为此,我们建议您参考他们在[sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html](http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html)的文档,并将重点关注许多主要特性和差异,因为否则只会重复他们的内容。我们将关注 SCSS 语法,而不是不太像 CSS 的老方法。

变量

变量在 Sass 中使用稍微不同的语法。最初,创建变量使用以下语法:

!variable = value

由于转向了更像 CSS 的语法,这种方法已被弃用。新样式类似于 LESS,但使用美元符号作为前缀:

$variable: value;

Sass 还使您能够在输出中直接包含变量值(称为“插值”),这允许您连接变量,甚至在选择器中使用它们。这是通过用大括号将变量名括起来并在前面加上一个散列符号来实现的。下面的 SCSS:

`\(id: product; \)property: color;
$value: green;

content .#{$id} {

#{\(property}:#{\)value};
}`

…编译成以下内容:

#content .product {   color: green; }

这使得变量在 Sass 中比在 LESS 中更加通用。默认情况下,Sass 也总是将输出格式化为多行,缩进两个空格,最后一个右括号在最后一行,而不是在新的一行。与 LESS 不同,Sass 理解变量的布尔值(真或假)。这使你能够使用条件逻辑,你可以在本章的后面读到。

images 提示: Sass 实际上支持四种不同的输出格式:压缩、压缩、扩展和嵌套。如果您正在使用命令行,您可以通过--style开关选择使用哪一个,如下所示:

sass test.scss test.css --style compressed

Sass 压缩非常好,实现 this 和@import 指令足以模拟连接和缩小的构建脚本。

在 Sass 中,变量实际上是变量而不是常数(这就是为什么在我们的例子中我们用 camel case 而不是大写来命名它们)。您可以根据需要多次更改变量值:

`\(color: red; \)color: green;
$color: blue;

content {

color: $color;
}`

…编译成:

#content {   color: blue; }

在 Sass 中,变量的作用域也有所不同。由于无法区分创建和修改变量,一旦创建了变量,任何在比初始范围更具体的范围内创建同名变量的尝试都将修改变量,而不是创建另一个变量。然而,作用域仍然受到重视,因为在某个作用域创建的变量在不太具体的作用域中不可用。这更容易用一个例子来解释:

`$color1: red;

content {

\(color1: green;    \)color2: blue;
   color: $color1;
   background-color: $color2;
}

.callToAction {
   color: \(color1;    //The following line is commented out, since it would cause a compilation error.    //\)color2 has not been created in the scope of this block.
   //background: $color2;
}`

…结果是:

`#content {
  color: green;
  background-color: blue; }

.callToAction {
  color: green; }`

在这种情况下,在.callToAction中引用$color2会导致编译错误,因为$color2还没有在该范围内创建(见图 9-7 )。

images

图 9-7。 Sass 报告了范围问题的明显错误

注意,如果命令行编译器遇到错误,Sass 将创建一个空文件,而不是完全忽略该命令。

嵌套选择器

Sass 中的嵌套选择器的工作方式与 LESS 中的完全一样,只有一个显著的区别:当将当前选择器的放在之后时,Sass 正确地使用了&组合符。这个 SCSS:

#content {    a {       .callToAction & {          color: red;       }       &.externalLink {            color: blue;       }    } }

...编译成以下内容:

`.callToAction #content a {
  color: red; }

content a.externalLink {

color: blue; }`

在这种情况下,Sass 的行为与我们预期的一样,而 LESS 会输出#content a .callToAction

LESS 和 Sass 都不会输出没有属性的规则。但是,它们都很乐意在同一个选择器中输出相同的属性和值对:

#content {    color: red;    color: red; }

…编译成:

#content {   color: red;   color: red; }

正如您所看到的,它重新格式化了代码,但是并没有减少不必要的代码。这同样适用于代码的压缩版本,包括 Sass 和 LESS。

条件逻辑

LESS 非常缺少条件逻辑和循环行为(将在下一节描述),Sass 提供了这些。支持 If 和 else 子句。您可以使用大括号来包含要计算的 SCSS;和@if、@else 和@elseif 作为条件命令。一个非常简单的例子如下:

`$christmas: true;

@if $christmas {
   #content {
      color: red;
   }
} @else {
   #content {
      color: black;
   }
}`

产生的 CSS 如下:

#content {   color: red; }

您还可以在选择器中使用 if 语句,就在单个属性周围。支持标准运算符:

| `==` | 等于 | | `!=` | 不等于 | | `<` | 不到 | | `>` | 大于 | | `<=` | 小于或等于 | | `>=` | 大于或等于 | | `and/or/not` | 用于组合条件的多个部分 |

这里有一个简单的例子:

`$lines: 10;

content {

@if $lines < 10  {
      height: 20px;
   } @elseif $lines == 10 {
      height: 40px;
   } @elseif $lines > 10 {
      height: 60px;
   }
}`

结果如下:

#content {   height: 40px; }

虽然这可能会使 SCSS 变得更加复杂和难以阅读,但它也使变量和 Sass 作为一个整体变得更加强大。

循环

Sass 支持两种循环方法:@for 和@while。对于从一个数字到另一个数字的简单迭代,@for 是最合适的。语法很简单,包括声明一个变量和定义循环的起点和终点。变量可以在每次迭代中正常使用。这里有一个例子:

@for $counter from 1 through 3 {    #product#{$counter} {          color: green;    } }

生成的 CSS 如下所示:

`#product1 {
  color: green; }

product2 {

color: green; }

product3 {

color: green; }`

Sass 还支持“从 n n”而不是“从 n n”,这仅仅意味着变量必须小于而不是等于最后一位数字。在我们的例子中,这会给我们两次迭代,而不是三次。

@while 循环适用于更复杂的迭代,例如,对于循环的每次迭代,您可能希望增加不止一个步骤。循环将继续,直到条件评估为假。同样,语法很简单:

$ counter:1;

@while $counter <= 6 {    #product#{$counter} {       color: green;    }    $counter: $counter + 2; }

结果如下:

`#product1 {
  color: green; }

product3 {

color: green; }

product5 {

color: green; }`

这些类型的控制指令是对 Sass 的一个非常受欢迎的补充(也是 LESS 中明显缺少的)。

正在评论

Sass 支持单行的、C 风格的注释,在这方面的行为与 LESS 完全一样。

访问者

Sass 使您能够使用@extend 关键字将选择器附加到前面的规则中——如果选择器只有一个元素的话。尽管要追加的选择器可以具有任何复杂性,但是它所应用到的规则不能有任何嵌套的选择器。下面的代码演示了这一点:

`.box {
   width:80px;
   height:80px;
}

div#content.main {
   color: red;
}

content #product {

color: black;
}

.redBox {
   @extend .box;
   color: red;
}

content div a.callToAction {

@extend div#content.main;
   text-decoration: underline;
}
.productBox {
   //The following line would cause an error
   //@extend #content #product;
}`

它编译成以下内容:

`.box, .redBox {
  width: 80px;
  height: 80px; }

div#content.main, #content div a.callToAction {
  color: red; }

content #product {

color: black; }

.redBox {
  color: red; }

content div a.callToAction {

text-decoration: underline; }`

您可以在一个声明中扩展多个规则。如果我们取消了标记行的注释,错误将如下所示:

Syntax error: Can't extend #content #product: can't extend nested selectors         on line 22 of style.scss   Use --trace for backtrace.

虽然复杂,这种技术可以是强大的。但是,如果处理特殊性问题,您必须小心,因为 Sass 在扩展时会重新排列选择器的顺序,并且很容易将选择器放在文件中比您预期的更早的位置。

混合蛋白

在 Sass 中,Mixins 遵循稍微不同的格式。在 LESS 中,几乎任何简单的选择器都可以用作基本的 mixin,而在 Sass 中,选择器是用@mixin 关键字和一个惟一的名称创建的,并且用@include 关键字引用,从而使 mixin 和规则非常独立。在某些方面,这可能被认为不太灵活,但它也鼓励模块化代码,使代码具有更清晰的意图,并避免您可能会面临的一些问题(例如,当不必要时,mixin 会在最终的 CSS 中输出,或者试图使用复杂的选择器重用 CSS 块作为 mixin)。这里有一个例子:

`@mixin border {
   border: solid 1px black;
}

content {

@include border;
}`

输出如下:

#content {   border: solid 1px black; }

在 Sass 中也可以将参数传递给 mixin,就像在 LESS 中一样,但是省略一个必需的参数会导致明显的错误。这里有一个真实的例子:

`@mixin borderRadius($radius) {
   -moz-border-radius: $radius;
   -webkit-border-radius: $radius;
   border-radius: $radius;   
}

content {

@include borderRadius(5px);
}

product {

@include borderRadius(15px);
}

.callToAction {
   //The following line would cause an error
   //@include borderRadius;
}`

它编译成以下 CSS:

`#content {
  -moz-border-radius: 5px;
  -webkit-border-radius: 5px;
  border-radius: 5px; }

product {

-moz-border-radius: 15px;
  -webkit-border-radius: 15px;
  border-radius: 15px; }`

如果我们取消了标记行的注释,由于没有为$radius参数设置默认值,将会发生以下错误(在 stdout 中):

Syntax error: Mixin borderRadius is missing parameter $radius.         on line 15 of style.scss, in borderRadius'
        from line 15 of style.scss
  Use --trace for backtrace.`

颜色

和 LESS 一样,Sass 支持颜色的数学运算。Sass 理解所有颜色格式,包括命名的颜色(LESS 不理解),并能正确地与 alpha 通道一起工作。Sass 还公开了许多特定于颜色的函数,这在 LESS 中是没有的。首先,有个颜色访问器,允许您公开颜色变量的特定属性:

  • red(color)

    images red()返回颜色的红色通道,为 0 到 255 之间的整数。

  • green(color)

    images green()返回颜色的绿色通道,为 0 到 255 之间的整数。

  • blue(color)

    images blue()以 0 到 255 之间的整数返回颜色的蓝色通道。

  • hue(color)

    images hue()返回一种颜色的色相,度数在 0 到 359 之间。 9

  • saturation(color)

    images saturation()以百分比形式返回颜色的饱和度。

  • lightness(color)

    images lightness()以百分比形式返回颜色的亮度。

  • alpha(color)

    images alpha()返回 0 到 1 之间颜色的 alpha 通道。 10

虽然hue()saturation()lightness()都有更少的颜色,但其他颜色访问器是 Sass 特有的。

使用这些组成完整颜色的独立部分,您可以基于另一种颜色的特定属性来构建颜色,这对于基于几个原色构建配色方案来说是一种非常高级的技术。

我们将展示几个使用这些属性的快速示例。首先,我们将剖析一个命名的颜色,并使用组成部分来构建一个 RGBA 颜色。这是 SCSS:

`$color: red;

\(red: red(\)color);
\(green: green(\)color);
\(blue: blue(\)color);
\(alpha: alpha(\)color);

content {

color: rgba($red, $green, $blue, $alpha);
}`

下面是编译后的 CSS:

#content {   color: red; }

有趣的是,Sass 选择将其作为命名颜色输出,这实际上是最有效的方法,也是最不容易解释的方法。它对所有可以被解析为命名颜色的颜色都这样做,这在读取输出的 CSS 时有时会更加混乱,但是您将在 SCSS 开发,所以除了在调试时,这应该不会出现问题。

事实上,Sass 总是将颜色转换成它认为最有效的输出。在可能的情况下,它会将它们输出为已命名的颜色。否则,它将使用十六进制颜色。最后,如果有一个阿尔法通道,它将输出 RGBA。这是另一个例子:

`$color: rgb(1, 2, 3);
  #content {
   color: $color;
}

$color: rgba(1, 2, 3, 0.5);

product {

color: $color;
}

$color: hsla(90deg, 50%, 50%, 0.5);

.callToAction {
   color: $color;
}`


9 计算一种颜色的色调非常复杂,需要运用一些相当高级的数学知识。你可以在[en.wikipedia.org/wiki/Hue](http://en.wikipedia.org/wiki/Hue)了解更多关于色相的内容。

10 为了向后兼容以前版本的 Sass,功能opacity()alpha()完全相同。

以下是输出结果:

`#content {
  color: #010203; }

product {

color: rgba(1, 2, 3, 0.5); }

.callToAction {
  color: rgba(128, 191, 64, 0.5); }`

对于开发人员来说,这是一个有趣的决定,知道这一点很重要。这是一个值得称赞的方法,因为它通常能产生最有效的输出。

Sass 还为您提供了颜色变异函数:修改颜色变量的值的函数。我们将列出其中的每一项,并对其进行简要描述:

  • lighten(color, amount)

    images lighten()允许您按百分比将颜色变浅。

  • darken(color, amount)

    images darken()允许您将颜色加深一个百分比。

  • adjust-hue(color, amount)

    images adjust-hue()允许你逐渐调整颜色的色调。这个和spin()里的少一样。

  • saturate(color, amount)

    images saturate()允许你进一步按百分比饱和一种颜色。

  • desaturate(color, amount)

    images desaturate()允许您按百分比进一步降低颜色的饱和度。

  • grayscale(color)

    images grayscale()将把一种颜色转换成它的单色等价物。

  • complement(color)

    images complement()将一种颜色转换成它的互补对应物。

  • mix(color, color, amount)

    images mix()将两种颜色结合在一起。如果提供了 amount 属性(以百分比的形式),它将使用第二种颜色的那部分。如果省略,Sass 会将两种颜色平均混合。

  • opacify(color, amount)

    images opacify()将按特定量(0 到 1 之间)增加颜色的不透明度(或降低透明度/alpha)。它将不会(明显地)增加超过 1 或低于 0 的值。 11

  • transparentize(color, amount)

    images transparentize()opacify()相反,将按照所提供的量(0 到 1 之间)增加颜色的透明度/alpha。像opacify()一样,萨斯绝不会让一个阿尔法通道超过 1 或者小于 0。 12

虽然lighten()darken()adjust-hue()saturate()desaturate()opacify()transparentize()的等效物都有,但都比较少;其他颜色变异是 Sass 独有的。

使用这些函数(以及我们之前提到的颜色访问器),可以实现非常复杂的颜色处理和计算。您可以生成一个完整的配色方案或变亮/变暗颜色,以便创建渐变,等等。一如既往,这里有一个简单的例子:

`$color: blue;

content {

background-color: \(color;    color: complement(\)color);
}`

结果如下:

#content {   background-color: blue;   color: yellow; }


11 为了向后兼容以前版本的 Sass,功能fade-in()opacify()完全相同。

12 为了向后兼容以前版本的 Sass,功能fade-out()transparentize()完全相同。

导入

Sass 支持@import,和 LESS 完全一样,只是有两个小的区别。Sass 不会将@import 命令移动到文件的顶部,这虽然不是一个明显的遗漏,但却是一个遗憾,因为它会导致无效的 CSS,其中@import 规则将不会被应用。当然,如果您打算直接将 CSS 作为输出的一部分,您只需要将文件后缀重命名为。scss(或。文件越少越好)。如果您使用如下命令:

@import "style.css";

Sass 会将其转换为以下内容:

@import url(style.css);

少了就不会这样了。这是一个不重要的改变,因为根据 CSS 2.1 规范,这两种语法都是可以接受的。但是,生成的 CSS 要大三个字符。因为@import 命令在 CSS 中并不常见(我们建议不要使用它们),所以我们觉得这没什么好担心的。

结论

Sass 还有许多其他特性,如果您打算将它作为开发过程的一部分,那么您应该通读大量编写良好的文档,并精通它们。

LESS than Sass 的应用越来越广泛——也许是因为它更简单,从一开始就看起来像 CSS 所以找到员工、应用程序支持和与它配合良好的插件可能会更容易。不可否认,Sass 更强大,但是,如果您选择使用这些预处理程序中的一个,您应该权衡每一个的利弊。

虽然不如 less 多产,但是存在用 Apache 在读取时编译 Sass 的方法,以及用于大多数主要可扩展 ide 和 Ruby gems 的插件。无论您的服务器设置如何,只要您愿意,您都可以将 Sass 集成到其中。

你可以在http://sass-lang.com/阅读更多关于 Sass 的内容。

尽管许多 CSS 开发人员对这两种技术(LESS 或 Sass)中的任何一种都有抵触情绪,因为他们认为这会带来太大的负担,但这确实是一种没有根据的批评。因为它们都支持 CSS 样式的语法,所以将原始 CSS 文件重命名为。少还是。scss 并添加您认为合适的新功能。真正的批评应该是对构建过程的修改、调试中的困难、对第三方的依赖以及所需的额外技能。然而,在您的组织中,您可能觉得这些问题是可以接受的,因为这些技术给您带来了许多好处。

评估第三方技术

在使用任何第三方代码作为开发过程的一部分之前,应该进行彻底的调查,以确保这些代码是适当的和安全的。尽管人们很容易对库、框架或预处理程序的能力感到兴奋,但是一旦它们被实现,以后要改变它们就很昂贵和困难了。因此,首先对它们进行彻底的研究,并确保团队同意使用它们是至关重要的。

需要考虑的一些事项如下:

  • Are many developers actively working on the code?

    如果只有一两个开发者,这应该是一个紧急的危险信号。很少的开发人员处理一段代码意味着很少的输入来源,这反过来意味着对用例或潜在问题的更狭隘的看法。它还表示风险,即开发人员可能会失去兴趣或由于某种原因不再能够对代码做出贡献。CSS 的世界变化(相对)很快,技术需要更新以适应这种变化。

  • Is the code mature (old)?

    没有被广泛使用的代码不太可能被很好地测试。尽管较新的技术往往是最令人兴奋的(我们当然鼓励尝试代码以获得想法或灵感),但我们不建议在生产系统或高容量网站中使用它们,在这些地方边缘案例可能会很多。

  • Is the technology in widespread commercial use?

    同样,在更多地方使用的代码可能更安全,也更容易测试。流行技术的结果是社区、第三方文档(如书籍和博客/网络文章)和行业热情(因此可以雇佣更多的开发人员)。

  • Is the source code available?

    作为对代码停滞不前且不再被开发的未来场景的最后一招,可用的源代码将允许您的公司在内部进行未来的修改。这剥夺了允许第三方代表您有效地测试和开发代码的好处,但是保护您免受一定程度的风险。

  • Is there comprehensive documentation?

    好的文档显然有助于技术的实现和使用,同时也展示了认真的开发,这是一个好的迹象。

  • Is there a test suite?

    测试套件的存在应该表明测试良好的代码,并减轻对未来版本中回归错误的担忧。

  • Are there change logs?

    当新版本的代码发布时,很可能会对旧的实现产生影响。清晰的更改日志准确地指出了哪些内容发生了更改,以及哪些内容可能会导致问题或影响您现有的代码库。旧的方法和语法应该被弃用,至少在几个版本中不能被删除,以便让技术的消费者有机会将他们的代码更新到新的接口。

如果您对正在评估的技术的大多数问题的回答都不是肯定的,那么您应该认真思考实现这项技术是否会成为未来技术债务的来源,以及您试图解决的问题是否无法用其他方法更好地解决。

使用服务器端技术提供 CSS 服务

正如我们之前提到的,目前所有的浏览器都不关心你的 CSS 文件有什么后缀。 13 只要你用正确的 mime 类型(text/css)提供包含你的 CSS 的文件,浏览器就会很高兴地阅读它,然后继续它的日常事务。这意味着您可以使用您选择的服务器端技术,为您提供与预处理器相同的功能,甚至更多。您可以使用 PHP 连接到数据库,从那里填充变量,并将它们直接插入到代码中;或者编写自己的 ASP.NET 颜色处理函数。只要引用一个包含 CSS 的文件并提供正确的 mime 类型,这就可以了。

一个示例实现是具有许多子品牌的大型网站。尽管 CSS 基本上是相同的,但是服务器可以生成多个版本的 CSS 文件,这些文件具有从数据库中填充的不同配色方案。该数据库可以通过 CMS 进行填充,从而实现网站与网站之间一致的自动化布局,以及可以由出版部门而不是开发人员制作的配色方案。

您也可以使用这种技术在不同的情况下提供不同的 CSS。例如,您可以检测用户工作的部门(如财务、人力资源、IT 等),并根据查询字符串或数据库条目向他们提供不同的 CSS。您还可以为用户提供他们独有的 CSS 尽管这种技术很难在服务器端缓存,并且会影响性能和带宽。更好的做法是为用户提供 CSS 选项或配色方案,并为他们提供有限数量的带有共享 URL 的 CSS 文件(或 querystring 配置)之一,以便可以有效地缓存结果。

与 LESS 和 Sass 相同的缺点也适用于此,或许程度更大。当试图调试你的 CSS 时,行号将与你的源代码不一致,选择器可能在你的源文件和结果文件之间看起来完全不同。也就是说,使用变量或数据库来维护代码的能力是允许非技术人员(如发布或内容团队)修改网站美学的一种很好的方式,自定义代码将使您对输出有更多的控制。当然,另一个不利之处是,很难雇佣廉价或低端的开发人员,因为学习曲线更陡峭,他们不能立即变得富有成效。

和往常一样,是否实现这种风格的 CSS 生成取决于您的特定场景。

持续集成(CI)

正如我们之前提到的,使用构建脚本来操作 CSS 是可能的,通常是出于性能的原因。虽然您可以手动运行这些脚本,但是能够以自动方式运行它们以部署到其他环境是很有用的。通常,一个组织应该有四个主要环境(可能每个都有几个):

  • Development

    虽然 CSS 作者通常在他们自己的机器上编写和测试 CSS,但当他们需要将他们的代码与其他人所做的更改集成,以更有效地测试他们作为项目的一部分所构建的内容时,他们会部署到开发环境中。这种环境通常是不可靠的,因为变化被非常频繁地部署到其中,因此对于团队进行高级测试是有用的,但对于彻底的或探索性的测试则不是。 14

  • Integration or QA

    此环境的部署频率较低,通常以预定的时间间隔进行。由于代码不经常更改,这是进行更彻底测试的好环境。通常,同时修改您的项目所依赖的代码的其他团队会使用这个环境来测试您的代码之间的连接。

  • Staging

    images尽管开发和集成环境很少是实际生产环境的精确副本,但试运行环境的行为和配置应尽可能接近生产环境。对此环境的任何部署都应该是可靠的、可靠的、经过测试的代码;在这一点上,应该执行用户验收测试(UAT)。这种环境通常也是发布或内容团队在将它们推向生产环境之前用来测试它们对站点的修改的环境。

  • Production

    这是为你的用户提供页面的实时网站。


其他文件类型也是如此,比如图像、JavaScript 或字体。

当使用版本控制时,通常只有当您确信您的变更将按预期工作时,才将其推送到主存储库。一旦您提交了它们,您可能想要做的第一件事就是将它们部署到开发环境中。

虽然可以简单地连接到适当的服务器,从源代码管理存储库中获取最新版本的文件,然后手动运行任何构建脚本,但是这非常耗时,因此不是一种有效的工作方式。

持续集成(CI)系统解决了这个缺点。可以将服务器设置为自动检测存储库中的变更——或者在预定义的时间间隔后通过轮询,或者通过版本控制系统中的钩子。每当检测到更改时,服务器获取文件的新版本,然后构建项目。成功构建项目后,它会自动部署到开发环境中。

这种方法有几个优点:

  • 任何测试都可以在项目构建之后、部署之前运行。这意味着,如果您有可以在客户端运行的测试,您可以构建一个临时环境,在服务器上自动运行测试——可能在许多操作系统和浏览器中——如果有问题或测试失败,选择不部署到开发环境。这被称为破坏构建。(你可以在第十章中读到更多关于测试的内容。)
  • 任何构建脚本都可以自动运行。
  • 所有用户都会收到关于任何构建问题的警告。如果有人破坏了构建,某种通知系统可以让所有其他开发人员知道,并且可以就谁应该修复它以及应该如何修复它进行对话。在问题解决之前,其他开发人员不应该提交存储库。
  • 同一系统可用于手动(或自动)部署到其他环境。

14 “探索性测试”描述了无脚本测试,测试人员依靠他们的知识在站点上“捣乱”,寻找意想不到的结果或错误。

通常使用交通灯系统,其中一个小应用程序驻留在用户的菜单栏或系统托盘中。如果构建成功,它会亮绿色;如果构建失败,它会亮红色。

如果您已经选择合并构建脚本来缩小或连接您的代码,CI 可以在它被设置为部署时为您运行这些脚本。然而,您应该尝试包含启用某种调试模式的能力(如下一章所述)——至少在开发环境中——以便当测试失败或发现问题时,它们易于解决。

您可以运行的另一个测试是查看站点的结果文件是否有效,或者是否有任何生成警告。在这种情况下,您可能仍然会选择部署,但是应该通知签入导致警告的文件的开发人员。

CI 鼓励开发人员养成各种好习惯。如果它与自动化测试相结合,它可以使代码更加健壮,并节省许多浪费的时间——以及更早地提醒开发人员和测试人员注意问题。

巧妙构建脚本

前端工程师杰德·托马斯(Jade Thomas)提出了自己的方法,一次性解决了许多问题,而不仅仅是缩小和连接预定义的文件列表。选择在她的 CSS 中使用特定于浏览器的供应商扩展作为 WebKit 的目标,构建脚本解析这些属性,并将其复制到同一属性的所有其他特定于供应商的版本中,从而提供一种抽象的方法来处理规范中的变化,并在出现变化时添加更多特定于供应商的扩展。接下来,为 IE 的版本设置一系列自定义的供应商前缀,如–ie6--ie7-等(不是–ms-,因为它已经存在),构建脚本可以解析并输出这些前缀,作为该浏览器的合适工具。

但是现在,聪明的部分。构建脚本实际上为 IE 的每个版本输出一个文件,为所有其他浏览器输出一个文件,而不是输出 hacks。然后,这些文件可以通过条件注释提供给适当的浏览器,这样每个浏览器都只能获得针对它的代码。LESS 或 Sass 这样的预处理器可以模拟一些这种行为,但远没有那么好。这种方法的好处是显著的:

  • 原始文件不需要编译就可以工作(在 WebKit 上)——这意味着当我们调试时,我们仍然可以看到有问题的 CSS 在哪一行。
  • 当验证工具支持厂商前缀时,我们的 CSS 实际上会进行验证! 十五
  • 输出中永远不会有黑客攻击。
  • 构建脚本的修改版本可以在不修改行号的情况下输出 IE 版本,这也使调试变得容易。
  • 每个浏览器只发出一个 HTTP 请求。
  • 每个属性的特定于浏览器的代码并存,使得维护这些文件变得容易。

最新版本的 W3C CSS 验证器([jigsaw.w3.org/css-validator/](http://jigsaw.w3.org/css-validator/))允许你将厂商前缀显示为警告,而不是错误。

遗憾的是,这种技术还没有公开的版本或文档,但是如果您的过程证明了这一点,我们鼓励您考虑模仿这种行为。

缓存注意事项

当提供任何类型的动态文件时,缓存都会成为一个问题。如果您提供的文件是经过预处理的,但在其他方面是静态的,并且提供给许多用户,那么您只需要配置您的 web 服务器,将这些文件视为可缓存的(并且您仍然可以使用版本控制—如前一章所述—在必要时有效地绕过缓存)。如果这些文件是在对用户做出反应的任何时候处理的,而不是在此之前,那么这些文件也可以通过不同的机制进行缓存。但是,对于您的用户来说,最好是在请求之前主动准备好类似这样的文件。这意味着没有一个人会承受性能影响的冲击,服务器会承受可预测的(而不是不一致的)压力。

如果文件是动态修改的(可能是基于每个用户),并且存在太多的潜在变化,预先创建这些文件是不合理的,那么从服务器端进行缓存就变得不切实际了(尽管浏览器仍然可以缓存内容),因此性能下降是不可避免的。为了解决这个问题,您可以尝试限制潜在的变化,缓存获得最多流量的变化,或者确保您的 web 服务器没有额外负载的问题。在这种情况下,边缘端缓存可以帮助最小化负担。边缘缓存目前只能在(X)HTML 标记中使用,但是我们在这里提到它是为了您的兴趣,因为它可能与您选择的缓存策略有关。

边缘端缓存使用一种简单的标记语言边缘端包含(ESI) 来动态请求响应的特定部分,并将其余部分作为静态内容。这需要由某种 CDN 或缓存服务器来实现和解析,以便用户点击它并返回正确的响应。一些组织已经实现了它,对于高性能的网站来说,这是一种非常有效的技术。

images 注:参见[en.wikipedia.org/wiki/Edge_Side_Includes](http://en.wikipedia.org/wiki/Edge_Side_Includes)支持 ESI 的厂商列表。

ESI 语言规范在 2001 年 8 月提交给了 W3C,但没有得到回应。然而,ESI 的反应和吸收是积极的,如果你想使用它,它是足够成熟和简单的,呈现最小的风险。一段非常简单的 ESI 标记如下所示:

<esi:include src="http://yourcompany.com/uncachable.content"/>

Varnish16支持 ESI,Akamai 等很多 cdn 和缓存服务器也是如此。你可以在[www.w3.org/TR/esi-lang](http://www.w3.org/TR/esi-lang)阅读更多关于 ESI 的信息。

总结

这一章解释了一些你可以拥有动态的和自我构建的 CSS 文件或响应的方法。这些技术并不总是合适的,并且在雇用新员工时会导致生产力下降,但是在正确的情况下可以使开发变得更加简单和愉快。如果它们适合你的网站,并且你已经考虑了这些方法的缺点,你就不应该担心实现它们。

下一章关注的是调试和测试,将教你需要知道什么来找到这种平衡,确保你充分利用你的资源,以及定位你的代码的问题并修复它们。


16 Varnish 是一款出色的软件,它极大地提高了许多 web 服务器的缓存能力。在[www.varnish-cache.org/](http://www.varnish-cache.org/)阅读更多信息。

十、测试和调试

所以,你已经建立并运行了你的网站,你已经写好了你的 CSS,所有的事情第一次都完全正确,对吗?不太可能。对于任何类型的脚本或编程,在达到目标的过程中都有一定程度的尝试和错误。这同样适用于 CSS。编写 CSS 时,开发人员通常在代码和浏览器之间来回切换,编写或修改代码,并刷新以检查结果。如果中间有任何更复杂的步骤——构建、上传、编译等等——开发人员的生产力会受到每次编辑的影响。

一旦代码完成,有必要确保它在网站支持的所有浏览器中都能正常工作。像这样的测试需要时间和精力——CSS 经常影响网站的很大一部分——所有可能受到影响的东西都必须测试。

无论何时发现问题或错误,都需要快速修复,并在最小影响和最大信心的情况下,确保它们不会在网站的其余部分引发新问题(或重现旧问题)。

本章将关注帮助你以最有效的方式实现这一切的工具和方法。我们将讨论的许多工具都有我们不会提及的功能,因为它对 JavaScript 或服务器端开发人员更有用;我们将关注 CSS 开发人员感兴趣的部分。您将了解以下内容:

  • 高效快速发展
  • 创建“调试模式”
  • 调试和工具
  • 自动化测试
  • 本地手动测试

快速发展

当您开始从事 web 开发时,您倾向于从事小型静态项目。这类网站在你电脑上的文件夹中运行得和在互联网上的服务器上运行得一样好。开发简单快捷,只需在编辑器和浏览器之间来回切换,无需担心外部依赖、第三方代码或其他开发人员和 CMS 对 CSS 的破坏。然而,在高流量网站的世界里,可以很有把握地假设您的开发实践受您的环境的影响要大得多。您的代码可能存在于多种环境中,并且需要额外的步骤来部署和测试您的代码。这违背了许多 CSS 开发人员的初衷,他们习惯于进行小的、迭代的更改,并频繁地测试这些更改。那么,如何才能找回自己如此热爱的快速开发周期和瞬间满足感呢?

第一步是找出影响你工作效率的事情。

构建连接 CSS 的脚本

如果您正在使用一个将多个 CSS 文件连接在一起的构建脚本——我们当然建议您这样做——这个过程可能需要一段时间,并且会造成障碍。当然,对于生产代码来说,这样做是正确的,但是对于开发环境来说,这样做的速度和性能增益可以忽略不计,因为您运行的服务器很可能与您的开发机器在同一个网络上。

一个好的解决方案是使用调试样式表。让我们假设构建脚本作为某种 shell 脚本运行,它包含了我们想要连接的所有文件。可能会有一个配置文件告诉 shell 脚本我们希望它加载哪些文件以及以什么顺序加载。我们得到的文件可能被称为“main.css”。不使用配置文件,您可以考虑使用 CSS 文件,因为您有@import 指令,它允许您使用多个文件实现相同的功能。如果您有一个名为“main-debug.css”的文件,它可能包含如下内容。

@import url(reset.css); @import url(global.css); @import url(login.css); @import url(feature.css);

使用一些巧妙的正则表达式,让我们的 shell 脚本读取这个文件并使用@import 指令构建“main.css”并不难。这给我们带来了两大好处:

  1. 文件列表只保存在一个地方。
  2. 您可以使用“main-debug.css”来模拟“main.css”的行为,但不需要运行构建脚本。

如果您随后修改页面以接受(例如)的 querystring?debugCSS=true,并使用该参数在包含的 CSS 的文件名中插入"-debug ",打开和关闭该行为变得很简单。现在很容易修改文件并立即看到结果。

构建压缩 CSS 的脚本

如果你遵循了我们的建议,你也在压缩你的 CSS。如果构建脚本将您的 CSS 压缩到一个名为“main.css”的结果文件中,就会出现与串联相同的问题。如果在文件名中使用“-debug”来命名原始文件,那么可以使用前面提到的 querystring 技巧来访问未压缩的文件。当然,如果您既要连接又要压缩,我们在上一节中提到的处理连接的 CSS 的方法在这种情况下同样适用。

不喜欢频繁刷新或超时的页面

如果您正在网站的某些部分工作,这些部分需要发布信息或具有安全连接,刷新这些页面可能会向您显示不同的内容或重定向到不同的页面。这意味着每次更改这些页面使用的 CSS 时,您都需要浏览回页面,可能需要再次登录或经历其他一些耗时的过程。

但是,有一些方法可以重新加载该页面的 CSS,而不需要重新加载页面的其余部分,假设 CSS 没有被 JavaScript 修改或者包含在页面本身中。如果您使用的是能够直接修改 HTML 的浏览器或具有相同功能的附加组件,如 Firebug、 1 ,您可以定位到外部文件的链接,并修改 URL 以包含具有唯一值的 querystring。例如,如果您的代码包含如下内容:

<link rel="stylesheet" href="/css/main.css" />

您可以将其修改如下:

<link rel="stylesheet" href="/css/main.css?12345" />

这应该会对文件提出新的请求。出于某种原因,您的 URL 可能已经包含查询字符串,例如:

<link rel="stylesheet" href="/css/main.css?hello=goodbye" />

然后,您只需要在它的末尾附加一个新参数来强制刷新并保持请求不被中断:

<link rel="stylesheet" href="/css/main.css?hello=goodbye&12345" />

但是,如果您继续使用这种方法,您附加的字符串将不会像您希望的那样唯一!无论你在键盘上敲击得多么随意,你都会很快地重复你自己,并开始从缓存中返回文件,而不是新的请求。为了解决这个问题,最好添加时间和日期,因为它们总是唯一的。不过,自己输入这些并不好玩,所以你可以使用 Firebug、Web Inspector、IE 开发工具或 Opera 蜻蜓中的控制台来运行一点 JavaScript 来为我们完成,就像这样:

var date = new Date().getTime(); var links = document.getElementsByTagName('link'); for (var i=0,j=links.length; i<j; i++){    var link = links[i];    link.href += (link.href.indexOf('?') == -1 ? '?' : '&')+date; };

该脚本将对它在页面上找到的每个link标签进行这些更改。也可以把它做成一个 bookmarklet,它是一小段代码,可以从浏览器(包括 IE)的收藏夹菜单中运行,以更友好、更快捷的方式实现同样的功能。

images 提示:这段代码确实有一些局限性,但是 Antony 在[zeroedandnoughted.com/bookmarklet-to-de-cache-css-and-images-version-2/](http://zeroedandnoughted.com/bookmarklet-to-de-cache-css-and-images-version-2/)写了一个功能更全面的版本。他的版本也将刷新图像和背景图像,并在当前文档和其中的框架或 iframes 内工作,但目前不支持使用@import 指令的文件。


1 Opera 允许你直接修改页面的源代码,然后从缓存中刷新。尽管使用起来不直观,但这种有用的行为是现成可用的。但是请注意,如果您直接从文件系统运行文件,它会修改文件本身。

缓存文件

由于在开发过程中缓存文件通常没有帮助,我们建议在开发时禁用浏览器中的缓存机制,但是在检查性能时不要忘记重新启用它。

网络浏览器的 bug

虽然不会严重影响您的工作效率,但在 Internet Explorer 中进行开发足以让大多数开发人员放弃,转而从事管道行业。我们建议首先在符合标准的浏览器中工作,然后再修复旧版本 IE 和其他浏览器中的所有问题。这能确保你快速到达一个好的工作点,并有助于保持你的理智。当然,了解您打算支持的浏览器的局限性会有所帮助,这样您就不会构建无法在其中正确显示的东西。我们将在第三章中详细讨论这一点。

调试

有时,您的代码会出现难以确定的问题。一个规则可能会覆盖另一个规则,或者包含的文件可能会影响到一些意想不到的事情。如果选择器似乎没有被应用,使用!important将背景颜色设置为红色、绿色或蓝色通常足以证明选择器正在工作, 2 但是如果它是您有问题的属性,解决起来不太可能这么简单。然而,有一些工具可以帮助我们解决这个问题。以下是一些内置于浏览器中或作为附加组件提供的工具列表。还有很多其他的。

Firebug(火狐)

Firebug 是第一个真正有用的前端 web 调试器。第一个公开版本(0.2)于 2006 年 1 月发布,此后突飞猛进,激励和推动浏览器厂商开发和提供自己的等效工具。因为 Firebug 是 Firefox 的一个扩展,所以有许多其他扩展插入其中,并进一步增强了它的功能,如监控 cookies、HTTP 头等的附加组件。尽管 Firebug(以及本章后面讨论的其他开发人员工具)提供了监控和调试前端 web 开发的所有方面的方法,但我们将集中讨论 CSS、HTML 和 HTTP 部分。

Firebug 可以“捕捉”到我们浏览器的主窗口,但是我们将在它的“弹出”窗口视图中查看它。首先是 HTML 视图(如图图 10-1 所示)。


使用彩色边框有相同的结果,但是修改了元素的大小和位置。

images

图 10-1。Firebug 的 HTML 视图

这个视图包含各种有用的信息和功能。在主窗口中,它显示当前页面的 HTML 文档,并允许您展开和收缩任何带有子节点的节点。该视图中的一些可能操作如下:

  • 将鼠标悬停在某个元素上,将在页面中突出显示该元素。
  • 将鼠标悬停在图像上,会在一个小窗口中显示图像及其尺寸(其他浏览器工具都不这样做)。
  • 单击一个元素将选择该元素。
  • 在工具栏中的编辑按钮旁边,所选元素的祖先显示为面包屑。单击其中任何一个都会高亮显示该父元素。
  • 单击任何元素属性将允许您重命名该属性;单击任何属性值将允许您更改属性的值。
  • 在编辑属性时按 Tab 键允许您循环到下一个属性名称或值(Shift-Tab 将循环到上一个),当您到达该元素的最后一个值时,再次按 Tab 键将允许您输入新的属性和值。
  • 单击文本节点将允许您修改该节点中的文本。

执行这些操作中的任何一个都会导致对文档对象模型(DOM)和页面的即时更改,您可以立即看到这些更改。

images 提示:你也可以在网页中选择元素,右击它们,选择 Inspect Element(这将自动显示 Firebug)。或者,您可以单击 Firebug 徽标旁边的图标,然后单击元素——这很有用,因为当您将鼠标悬停在元素上时,它们周围会显示蓝色边框,这以快速的视觉方式展示了元素所占用的布局和空间。

点击工具栏中的编辑按钮将允许你在一个内置的简单文本编辑器中编辑该元素的 HTML(包括外部 HTML)整体(见图 10-2 )。同样,这些更改将立即生效。再次单击编辑按钮将返回到上一视图。

images

图 10-2。Firebug 的 HTML 视图中的编辑模式

在右侧,所有应用于高亮元素的作者 CSS 选择器被显示,从上到下递减。继承的值如下所示。每个规则旁边都有一个链接,指向声明它的样式表,行号显示在括号中。在以“Inherited from”开头的一行是一个链接——将鼠标悬停在该链接上将高亮显示页面中匹配的元素,单击它将在 Firebug 中高亮显示这些元素,并在 HTML 中将焦点切换到它们。

对于这些规则中的任何一个,您可以单击任何属性或属性值来更改它,并立即在页面中看到这些更改。当鼠标悬停在它们上方时,一个有一条线穿过的圆会显示在该属性的左侧;单击此处将禁用该规则的该属性。从规则的最后一个属性值中跳出来将允许您输入一个新的属性值,或者您可以双击选择器上的任意位置或属性旁边的任意位置来实现相同的目的。当输入新属性时,Firebug 会为您添加冒号和分号,所以省略它们,否则它们将被视为参数名或参数值的一部分。

在另一个更具体的选择器中被覆盖的任何属性都被直观地删除。

默认情况下,这个视图不显示用户代理 CSS(Firefox 本身应用的规则)。通过单击样式旁边的向下箭头,您可以选择显示它们,从而选择它们在层叠中的应用位置。用户代理 CSS 在规则旁边显示有红色的“”,以使区别更加明显。Computed 选项卡将显示应用于元素的任何 CSS 的计算值(例如,实际的像素字体大小),分为文本、背景、框模型、布局和其他部分(你可以在第三章中阅读更多关于计算值的内容)。如果您想快速地将一个属性添加到一个元素中,您可以右键单击选择器并选择“编辑元素样式…”——这相当于将一个样式属性添加到元素中,当然,我们并不推荐这样做,但是这对快速测试很有用。

images 提示:通过点击活动标签旁边的向下箭头,可以在 Firebug 中访问其他有用的选项,例如查看和编辑方法:悬停或:链接的活动状态,否则很难访问这些选项。

布局选项卡显示所选元素的大小、填充、边框和边距,以及位置和z-index(参见图 10-3 )。将鼠标悬停在其中任何一个值上都会在页面中直观地显示出来,单击其中任何一个值都可以让您编辑它们并立即看到变化。这些更改将只影响所选的元素,因为它们将在 HTML 中的元素上显示为内联样式。

images

图 10-3。Firebug 的 HTML 视图中的布局标签

images 提示:在 Firebug 中,任何时候你发现自己在修改一个数值,你都可以使用键盘上的向上和向下键快速递增和递减整数中的值(一次一个整数),同时保持单位值(px 或 em)。

我们将忽略 DOM 选项卡,因为它主要关注 JavaScript。

CSS 视图显示每个 CSS 文件的全部内容,并且可以像 HTML 视图中的样式侧栏一样进行编辑(见图 10-4 )。它还包括一个使用内置文本编辑器的编辑按钮。在这个视图中,选择器也是可编辑的,这在 HTML 视图中是没有的。这种视图作为整个文件的整体视图是很有用的,但是 HTML 视图通常更有用。

images

图 10-4。Firebug 的 CSS 视图

Net 视图显示请求当前页面时加载的所有资源。这里提供了丰富的信息(见图 10-5 )。在一行级别上,您可以看到任何花费了异常长的时间来加载和阻塞其他资源的资源。您可以使用顶部的按钮根据文件类型进行过滤(显然,作为一名 CSS 开发人员,您最感兴趣的是完整视图,或者是深入查看 CSS 和图像)。

images

图 10-5。萤火虫的网景

将鼠标悬停在某个资源的任何时间线上(参见图 10-6 )会显示该特定请求的更多细节(这在第八章中有详细介绍)。有时,此视图会在发送前显示阻塞(或在旧版本中显示排队),这意味着浏览器已经用完了与该域的所有可用同时连接,并且正在等待其他资源下载,然后再下载该文件。这些细节对于发现哪些因素导致你的网站表现不佳是非常宝贵的。

images

图 10-6。悬停在 Firebug 网络视图中的时间线上

展开任何资源项目都会显示该特定请求的更多细节(参见图 10-7 )。第一个选项卡 Params 显示了任何发布的信息,或者作为查询字符串包含在请求中的信息。第二个选项卡显示 HTTP 标头,这些标头对于以下用途很有用:

  • 定位缓存头以确保它们正常工作
  • 检查随请求发送的任何 cookies,这是 CSS 文件的一个性能问题,应该避免
  • 检查响应是否被压缩(内容编码),这是一种性能增强

images

图 10-7。在 Firebug 的网络视图中查看请求的详细信息

其余的选项卡以不同的形式向我们展示了实际的响应。

你可以从[getfirebug.com/](http://getfirebug.com/)获得 Firefox 的 Firebug。尽管它是跨平台的,可以在 Windows、OS X 和 Linux 上运行,但目前它只适用于这些操作系统上的 Firefox。Firebug 的跨浏览器版本正在开发中,计划在 2011 年发布。

一个名为 Firebug Lite 的 Firebug 版本可以在[getfirebug.com/firebuglite](http://getfirebug.com/firebuglite),获得,它可以通过一个 bookmarklet 访问,在其他浏览器中提供许多与 Firebug 相同的功能,最明显的省略是 Net 选项卡。

Firebug 的一些最佳可用扩展(可能会使 Firefox 不太稳定)有:

  • 用于管理 cookie 的 Firecookie
  • Firediff 来查看在 Firebug 中对页面所做的更改
  • 查看输入的 CSS 选择器(或 XPath 表达式)会选择哪些元素
  • Codeburner 显示 HTML 和 CSS 的参考资料
  • 像素完美地覆盖图像,以帮助元素与所提供的屏幕图像对齐
  • 使用 CSS 来隔离未使用的选择器
  • YSlow 提供关于如何提高页面性能的建议

因为这个列表中的许多其他工具基本上都是模仿 Firebug 的,所以我们将只详细讨论它们的功能差异。

网络开发人员(火狐或 Chrome)

尽管 Web Developer ( [chrispederick.com/work/web-developer/](http://chrispederick.com/work/web-developer/))本质上并不是 Firebug 或调试工具的竞争对手,但它提供了一些额外的功能,值得一提(参见图 10-8 )。

images

图 10-8。Firefox 中的 Web 开发者扩展

快速禁用/启用缓存、JavaScript、cookies、CSS 和图像的能力通常证明了自己是一个有价值的时间节省者。方便地验证 CSS 和 HTML 是有用的,在预定义的窗口大小之间切换以测试多种分辨率也是提高生产率和节省时间的有效方法。

开发者工具(Internet Explorer 8)

Internet Explorer 8 提供了开发者工具,以努力使浏览器在调试方面达到标准。从视觉上看,它旨在模仿 Firebug,但可用的功能确实不同。它也可以停靠在主窗口上,或者有自己的窗口。

Developer Tools 包含了几个 Firebug 没有的功能,事实上它在模仿 Firebug 和 Web Developer 的功能方面走得很远。JS、CSS 和 images 的禁用/启用只需两次点击;缓存和 cookies 也是如此。验证 HTML 和 CSS 很容易,您可以生成一个“图像报告”——当前页面上所有图像及其各自文件大小、尺寸、alt属性等的列表——这对 SEO 和可访问性非常有用。

图 10-9 到 10-11 是在一台 Windows XP 机器上拍摄的,被置于“丑陋模式”(所有闪亮的塑料都被关闭)以使它稍微快一点。除此之外,它们应该与你的经历相符。

HTML 标签的左窗格(见图 10-9 )类似于 Firebug 的,但是没有添加新属性的能力。要添加新的属性,需要使用右窗格中的属性选项卡,以及自动完成条目的下拉菜单。可以在内置的文本编辑器中编辑 HTML,但不能编辑单个的片段或元素。同样,编辑过的 HTML 只是在body标签里面的 HTML 除此之外,不可能编辑任何 HTML。

images

图 10-9。Internet Explorer 8 开发者工具的 HTML 视图

右窗格的 Style 选项卡中的规则可以扩展和收缩,但是选择器本身不能编辑,这反映了 Firebug 的行为。在这个视图中可以启用/禁用整个选择器(在 Firebug 中不能)以及单个属性。无法在此视图中添加新的属性/值。

“跟踪样式”面板显示应用于当前元素的所有 CSS 的结果,不包括选择器(所有非默认属性)。

布局面板的行为与 Firebug 完全一样,但是没有有用的悬停行为(见图 10-10 )。您可以单击来编辑大多数值。它还显示偏移值(Firebug 没有),这可能对您有用。

images

图 10-10。ie 8 开发者工具 HTML 视图的布局标签

CSS 标签几乎完全模仿 Firebug 的标签,单个样式表的全部(重新格式化的)内容是可见的和可编辑的(见图 10-11 )。选择器在这个视图中是可编辑的,并且您可以通过一个复选框来启用/禁用整个选择器,除了 Opera 蜻蜓之外,这个列表中的任何其他工具目前都无法做到这一点。

images

图 10-11。ie 8 开发者工具的 CSS 视图

没有与“净”选项卡等效的选项卡。虽然你可以用 Fiddler 或 Charles(本章后面会提到)来弥补这一点,但是需要不同的工具来实现这一点会降低你的效率和生产力。

总的来说,这个工具比以前提供的有用得多,但仍然没有视觉吸引力,感觉不完整。可以是一个量子工具;有很多情况下,使用开发者工具实际上会大大降低 IE 的速度,或者导致它崩溃。

Internet Explorer 9 的开发工具(虽然仍处于测试阶段)看起来是一个很大的改进,现在包括了一个相当于 net 选项卡的功能,但是它们在不同版本之间变化很大,在这里详细介绍它们是没有意义的。

网页检查器(Safari)

如果你在 Safari 中启用了开发菜单,你可以访问这些工具的苹果版本:网页检查器 3 (见图 10-12 )。一般来说,Firebug 中所有可用的相同功能在这里都有,虽然感觉不太成熟。然而,在某些地方,Firebug 是开源的,Web Inspector 背后有资金支持,这一事实变得非常明显。特别是,Resources 选项卡(相当于 Firebug 的 Net 选项卡)看起来更加精致,尽管您需要在请求和 Resources 视图之间来回切换才能看到所有细节。

images

图 10-12。 Safari 的网页检查器

Safari 现在支持扩展,但在编写本文时,还没有针对 Web Inspector 的扩展。不过,WebKit 正在突飞猛进地发展,似乎在某个时候,它将在功能和可用性方面超越 Firebug。


3WebKit 的 Web Inspector 的 wiki 可从[trac.webkit.org/wiki/WebInspector](https://trac.webkit.org/wiki/WebInspector).获得

images 注意:有趣的是,Web Inspector 实际上是用 HTML、CSS 和 JavaScript 编写的,这意味着你可以检查 Web Inspector 本身背后的代码。

开发者工具(Chrome)

Chrome 的等价物可以通过进入视图>开发者>开发者工具找到。因为 WebKit engine 在 Safari 和 Chrome 的检查器之后,所以它们几乎是相同的。然而,Chrome 是一个更新的版本,因为谷歌更频繁地发布 Chrome 版本(见图 10-13 )。

images

图 10-13。 Chrome 的开发者工具

CSS 的显示是最明显的区别。Chrome 显示了属性周围的括号,因此看起来更友好,更整洁,但这意味着规则不可扩展/收缩。

images 提示:在 Safari 的 Web Inspector 和 Chrome 的开发者工具中,都可以双击选择器并直接修改它们,这是 Firebug 或其他工具中的样式窗格(在编写本文时)无法实现的。

Internet Explorer 开发者工具栏

对于 IE 6 和 IE 7,如果你能在你的站点上使用 Firebug Lite 的话,它通常是一个不错的选择。然而,开发者工具栏可以从[www.microsoft.com/downloads/en/details.aspx?FamilyID=95e06cbe-4940-4218-b75d-b8856fced535](http://www.microsoft.com/downloads/en/details.aspx?FamilyID=95e06cbe-4940-4218-b75d-b8856fced535)下载(搜索它可能比在浏览器中输入更简单!),确实提供了一些有用的特性。

images

图 10-14。 Internet Explorer 开发者工具栏

火狐开发者工具栏提供的一些功能是可用的;对元素应用样式的只读视图也是可用的,但是工具栏不会与列表中的其他项目竞争。但是,如果没有其他选择,它确实提供了一些有用的调试选项。

蜻蜓歌剧院

Opera 蜻蜓是 Opera 提供的产品,它为我们提供了调试功能,否则我们就会求助于 Firebug(见图 10-15 )。由于它是直接从 Opera 作为离线应用程序提供的,最新的版本总是可以无缝下载。当这本书发布时,蜻蜓 1.0 将会推出,所以我们使用了一个实验版本来尽可能多地模拟 1.0 版本的功能 4

images

图 10-15。歌剧院蜻蜓

Opera 蜻蜓比名单上的其他产品都年轻,在撰写本文时,它仍处于发展的初级阶段。它提供了内置功能,如颜色选择器、屏幕放大镜和存储颜色管理器,这些功能是其他产品在没有附加组件的情况下无法提供的。其他一些细节也很不错,比如从突出显示的元素中突出出来的水平和垂直规则有助于对齐。

事实上,它正在开发中,这在一些容易发现的直接错误中是显而易见的,但它肯定是值得关注的,并且处于快速迭代开发中。

所有主要的浏览器供应商现在都提供这些工具,这是一件好事,表明他们理解开发人员的需求,并打算提供这些工具。它们都很有用,并在每个浏览器中为我们提供了可以对您的工作流程做出巨大改变的功能。然而,很难推荐任何一款感觉最成熟、功能最强大、可扩展性最强的 over Firebug,不管你更喜欢哪种浏览器。


4 关于启用实验版的说明可以在[my.opera.com/dragonfly/blog/getting-opera-dragonfly-ready-for-opera-11/#enable](http://my.opera.com/dragonfly/blog/getting-opera-dragonfly-ready-for-opera-11/#enable)找到。

代理工具

尽管 Firebug Net 选项卡或等效工具为我们提供了查看 HTTP 请求及其组成部分的能力,但代理工具在较低的级别拦截流量,并为我们提供比其精简版浏览器更多的功能。有许多产品可以实现这一点,但很少有人能在调试能力上与 Charles 和 Fiddler 竞争。

提琴手

Fiddler 2 是微软的 Eric Lawrence 开发的免费 Windows 程序,最初于 2003 年发布(见图 10-16 )。它的成熟使它成为一个非常强大和完美的产品。它使用。NET 微软开发框架(2.0 版或更高版本;Fiddler 1.3 适用于 1.1 版本的用户,因此您需要安装它才能使用 Fiddler。从[www.fiddler2.com/](http://www.fiddler2.com/)开始提供。

images

图 10-16。提琴手

Fiddler 的组件太多了,本书无法一一介绍,但我们将提到对 CSS 开发人员有用的最常见的特性。本质上,Fiddler 位于您的浏览器和您的浏览器请求信息的服务器之间。这允许它检查在这两点之间通过的任何流量(包括安全流量)。

屏幕分为两个垂直窗格:第一个显示通过 Fiddler 的流量,第二个允许您进一步检查该流量。在文件菜单下,您可以选择启用或禁用流量捕获(是否在左窗格中显示请求)。

images 提示:您也可以通过按 F12 快速切换流量捕捉。

当 Fiddler 捕获流量时,每个请求将按照请求的顺序显示在左侧窗格中,包括 HTTP 状态代码、协议、主机(域)、URL、主体(内容的大小)、缓存细节、内容类型和请求它的进程(例如,iexplore)。突出显示该请求将在右窗格中显示更多信息。

许多网站发出的 HTTP 请求比我们预期的要多得多,并且会很快填满左窗格(还有你电脑的内存)。右边的 Filters 选项卡允许您准确地指定您想要显示和捕获的请求类型,这将帮助您专注于您所关心的请求,而不是将它们淹没在噪声中。

Statistics 选项卡提供了关于所选请求所用时间的非常详细的信息,甚至给出了不同互联网速度和地理位置的性能估计,这对于定位性能问题非常有用。

Inspectors 选项卡是最有用的选项卡之一,它提供了所有的标题信息,以及查看响应数据的多种方式。对于请求,您可以看到标头、文本数据、表单数据、十六进制请求、身份验证信息、原始数据以及请求中提供的任何 XML。对于响应,您可以看到标题、文本数据、图像数据、十六进制响应、基本 web 预览、身份验证信息、缓存信息、隐私数据(例如,正在创建/修改/删除的 cookies)和原始响应。如果响应是压缩的,Fiddler 会警告你,替你检测算法,给你呈现解码后的数据。

这些全面的视图主要用于 CSS 开发人员检查意外的大文件、压缩是否正常工作、预期的数据是否被返回以及数据是否被正确缓存。

自动回复标签可能是 Fiddler 提供的最有用的功能。调试时的一个常见问题是,一个环境中的 CSS 文件或 HTML 响应与其他环境不匹配,或者其他一些因素干扰了我们的预期结果。要绝对确定一个 CSS 变化将修复我们的目标环境中的问题是非常困难的。理想情况下,例如,如果我们在生产环境中替换一个文件,我们希望知道确切的结果。自动回复可以让你做到这一点。您可以提供一个 URI 来匹配(例如,[www.mycompany.com/css/style.css](http://www.mycompany.com/css/style.css))和一个响应。Fiddler 为各种 HTTP 状态代码提供了典型的响应,但它也允许您在本地机器上选择一个文件。这样做允许您用自己的文件替换任何服务器上的单个文件,这对于客户端开发人员来说是一种非常好的开发方式,因为他们不再需要关心服务器或底层基础结构,而是可以自信地独立处理特定的文件。

所提供的 URI 格式并不像看起来那么简单,它可以以“EXACT:”和“regex:”作为前缀,以及其他用于不同模式类型的前缀。也不可能重定向整个文件夹(例如,匹配[www.mycompany.com/css/](http://www.mycompany.com/css/)并重定向到类似c:/projects/mycompany/css的本地文件夹)但保持文件名不变,这样[www.mycompany.com/css/1.css](http://www.mycompany.com/css/1.css)重定向到c:/projects/mycompany/css/1.css , [www.mycompany.com/css/2.css](http://www.mycompany.com/css/2.css)重定向到c:/projects/mycompany/css/2.css等等。

为了解密 HTTPS 流量,Fiddler 在你的电脑上安装了一个证书,这是一个潜在的安全风险,但却是一个必要的方法。您可以在工具> Fiddler 选项中启用它。由于 Fiddler 向您的浏览器提供所有数据,它也能够“节流”这种流量,并模拟较慢的互联网连接。

查尔斯

尽管没有 Fiddler 成熟,Charles 很快成为一个竞争者(见图 10-17 )。Charles 可以在 Windows、OS X 和 Linux 上使用,它比 Fiddler 提供的特性集更好,而且更容易使用。它不是免费的,但也不贵,你可以在[www.charlesproxy.com/](http://www.charlesproxy.com/)下载 30 天的免费试用版。

images

图 10-17。查尔斯

Charles 的跨平台特性是一大优势;你只需要学习一次它的界面,这些经验将适用于你选择使用的任何操作系统。这个界面比 Fiddler 的更有吸引力,也不那么吓人,而且它建立在一些可用的功能之上。

要进入录制模式(Fiddler 认为是捕捉模式),可以在窗口顶部切换一个红色的大圆圈按钮。当处于记录模式时,记录显示在窗口的右下角,Charles 将截取所有通过 web 浏览器请求的 HTTP 数据。在初始安装过程中,Charles 会为 Firefox 安装一个插件,以便能够拦截来自那里的数据。

images 提示:要检查 HTTPS 流量,需要添加代理下的主机>代理设置> SSL 并安装证书。这方面的说明可在http://www.charlesproxy.com/documentation/using-charles/ssl-certificates/获得。

查看数据有两种主要方法。第一种是在结构视图中,它按主机对所有内容进行分类,以便很容易看到哪些请求来自哪个主机。另一个视图(之前演示过)是 Sequence 视图,它类似于 Fiddler 的默认视图,按顺序显示每个单独的请求。在这个视图中,文本框使得输入过滤字符串变得很简单。任何不包含输入字符串的项目都是隐藏的,这是查询数据的一种快速而直观的方式。在这个文本中也可以使用正则表达式。

在任一视图中,突出显示单个请求会在选项卡式窗格中显示更多信息——在结构视图中位于右侧,在序列视图中位于下方。第一个选项卡是 Overview,类似于 Fiddler 中的 Statistics 选项卡。

接下来的选项卡是请求和响应,分别代表请求和响应数据。根据数据的不同,这些视图的底部可能会显示其他选项卡,包括标题、文本、十六进制、压缩、HTML、查询字符串、Cookies、Raw 和 JSON。信息以比 Fiddler 更直观的格式命名和呈现,这对 CSS 开发人员可能更有用。HTML 在那个视图中是用颜色编码的,尽管不幸的是没有特定于 CSS 的视图。

“摘要”选项卡显示突出显示的请求的基本详细信息,以及作为对该请求的响应而触发的任何请求。例如,突出显示一个 HTML 请求将显示该页面指定的所有资产。这对于高级视图很有用,但是图表选项卡更详细,类似于 WebKit 的 Web Inspector 或 Firebug 的 Net 选项卡。此选项卡还提供了默认时间线视图的其他视图,如与原始请求相关的所有请求文件的可视比较大小、这些请求的持续时间,以及可视演示所请求文件的类型及其数量的类型。

通过切换屏幕顶部的方格标志图标,可以启用和禁用带宽限制。这方面的设置可以在代理>节流设置中设置,有几个有用的预设以及输入您自己的自定义值的能力。

隐藏在工具菜单下的是 Charles 提供的真正的宝石。Map Remote 允许您将任何远程请求重定向到任何其他远程目的地,这对于将一个环境的资源映射到另一个环境非常有用,可能是为了在一个服务器上使用一个特定的 CSS 文件或文件夹,或者替换另一个服务器上的文件或文件夹。Map Local 相当于 Fiddler 的自动回复器,但是它具有更友好的匹配方法和重定向整个文件夹以及文件的能力。Rewrite 选项允许我们以几乎所有可以想到的方式重写请求或响应的内容,以匹配位置,包括替换正文中的字符串或添加新的头。

images 提示:要检查来自其他设备的流量,将它们与运行 Charles 的机器放在同一个网络上,并手动将该机器设置为设备上的代理服务器。

除了 Charles 提供的额外功能外,它还让人感觉更加精致。当你粘贴一个 URL 并在所有字段中填入代表你的各个部分时,Charles 足够聪明地将 URL 分开,这让你感觉这是一个很好的、可靠的软件。Fiddler 中嵌入的一些更深层次的功能超出了 Charles 的能力,但对于 CSS 开发人员来说,Charles 觉得更合适。

测试

测试是制作一个运行良好、在不同浏览器间外观和工作一致的健壮站点过程中的重要一步。不幸的是,有效地测试 CSS——网站的表示层——是一件非常困难的事情。尽管测试网站的其他层都可以以自动化的方式完成——JavaScript 可以有单元测试,服务器端代码可以有单元测试,HTML 可以被解析和验证——CSS 依赖于浏览器如何呈现它,并且需要由人工进行视觉检查。

从理论上讲,有一个页面应该如何呈现的截屏是可能的,并且作为自动化过程的一部分,将这个截屏与不同浏览器中的实际截屏进行比较。可以定义一个可接受的误差范围,根据两个截图之间的差异,测试可以通过也可以失败。然而,一个网站不可能也不会在不同的浏览器上看起来一样,这是事实。浏览器处理各种属性、字体呈现甚至图像呈现的差异确保了无论多小,不一致性总是存在的。两个文件的比较永远不会考虑哪些不一致是重要的,哪些是可接受的,所以这种测试方法永远不会完全准确。

然而,帮助这一过程的自动化技术确实存在。尽管通常测试人员需要访问每个需要测试的页面,但是在每个受支持的浏览器中,您可以使用技术来代表您生成屏幕截图,测试人员可以快速连续地查看这些截图,并且通过将两者叠加,可以很容易地与主示例进行比较。首先,有必要知道在这种情况下什么是通过或失败。

分级浏览器支持

因为完美地支持每个浏览器是不实际的,所以有必要提出一个矩阵来显示哪些浏览器被支持以及支持到什么程度。该矩阵的内容因地点而异。典型网站的流量主要由最新版本的 Internet Explorer 构成。技术含量更高的网站可能会有更高比例的访问者使用被认为更先进的浏览器,这些浏览器基本上都是当前的浏览器,在较小程度上也是当前版本的 Internet Explorer。在处理高流量网站时,很可能即使是占流量一小部分的旧浏览器也仍然代表着大量的用户。决定你支持哪种浏览器取决于你网站的流量,这应该被跟踪和报告以帮助你做出决定。

保持更新的一个很好的示例矩阵是 Yahoo!分级浏览器支持,可在[developer.yahoo.com/yui/articles/gbs/](http://developer.yahoo.com/yui/articles/gbs/)查看。编写时的当前版本如图 10-18 中的所示。

images

图 10-18。雅虎!分级浏览器支持

你决定的矩阵可能与这个有很大不同。例如,许多网站现在从 Windows 上的 Opera 或 Safari,或 OS X 上的 Chrome 看到了合理水平的流量,一些网站从 IE 6 看到了低水平的流量。

我们将在第六章中详细讨论分级浏览器支持。一旦你决定了你接受的浏览器矩阵,有必要确保你(和测试人员)有能力使用你打算测试的所有浏览器。

运行多个版本的 Internet Explorer

大多数浏览器(移动设备除外)都很容易并排安装,只要你有 Windows 安装和 OS X 安装(可能还有 Linux)。这可以通过虚拟机(允许用户在其当前操作系统内运行其他操作系统的软件)来实现,尽管你不能合法地在非苹果硬件上运行 OS X。IE 以外的浏览器更新速度很快,因为这些浏览器的用户通常会很快升级到最新版本。但是,要测试多个版本的 Internet Explorer,还需要采取其他措施。

尽管 IE 的每个版本都有一个独立的虚拟机是可能的(事实上,这是测试人员应该做的),但这对 CSS 开发人员来说是多余的。一款名为 Multiple IEs 的软件很早就解决了这个问题,允许同时安装 IE 版本 3 到 6。 5

这种方法有一些警告,主要是由于稳定性、过滤器和 cookie 处理,但对于旧版本 ie 中的页面快速视觉检查来说已经足够了。

多个 ie 上的开发已经停止,但是核心服务已经开发了一个名为 IETester 的 Windows 应用程序(见图 10-19 )。IETester 本质上提供了与多个 IE 相同的功能,,但是在一个带标签的窗口中,可以代表从 5.5 开始的任何版本的 IE。尽管这是一个 alpha 版本(从 2008 年就开始了),但是产品的开发仍然是活跃的,并且稳定性足以满足测试目的。它可能不稳定——有几个已知的问题——但对于 CSS 开发人员来说,它为提供相同功能所需的多个虚拟机提供了一个有用且有能力的替代品。


5 虽然 Multiple IEs 不再是活跃的开发,但是如果你感兴趣的话可以在[tredosoft.com/Multiple_IE](http://tredosoft.com/Multiple_IE)找到它。

images

图 10-19。 IETester

IETester 也可以配置为使用 IE 9,但这需要 Windows Vista Service Pack 2 或 7(或更高版本)。可以从[www.my-debugbar.com/wiki/IETester/HomePage](http://www.my-debugbar.com/wiki/IETester/HomePage)下载。

images 注意:一款名为 ies4osx 的应用也允许你在 osx 下运行 IE 的版本,但它需要 Darwine(一种 Windows 模拟器)或类似的软件,而且安装起来很麻烦。也就是说,如果你想玩它,你可以从[www.kronenberg.org/ies4osx/](http://www.kronenberg.org/ies4osx/)下载。

仿效其他设备

拥有测试网站所需的所有设备并不总是现实的,有时从这些设备访问开发环境可能是一件痛苦的事情。为此,这里有一个快速的模拟器列表,您可以使用它在台式计算机上模拟这些设备:

  • 苹果 iOS SDK : http://developer.apple.com/devcenter/ios
    • 该工具允许您模拟 iPhones、ipad 和 iPod Touches。然而,它不是免费的,只能在 OS X 下运行。
  • Android : [developer.android.com/sdk](http://developer.android.com/sdk)
  • 歌剧迷你模拟器 : [www.opera.com/developer/tools/](http://www.opera.com/developer/tools/)
  • 火狐手机 : [www.mozilla.com/en-US/mobile/download/](http://www.mozilla.com/en-US/mobile/download/)
  • 完美移动 : [vf.perfectomobile.com/np-cas/login](https://vf.perfectomobile.com/np-cas/login)
    • 该工具允许您在真实的移动设备和网络上进行远程测试。这个链接是沃达丰版本的,注册沃达丰的开发者是免费的。

自动生成截图

因为测试 CSS 通常需要一个人,所以让这个过程更有效的最好方法是让一个软件为你生成屏幕截图。有许多工具可以帮助解决这个问题。让我们来看看最常见的三种。

Selenium 是一个广泛使用的集成测试工具(见图 10-20 )。Selenium 目前是第 2 版,对原来的版本进行了重要的改进。它最初是作为 ThoughtWorks 的内部框架开发的,但后来已经成为一个完全独立的系统,有许多贡献者。一个名为 Selenium IDE 的 Firefox 插件允许您创建测试,例如:

  • 浏览至[mycompany.com](http://mycompany.com)
  • 等待页面加载
  • 检查特定div中的文本是否包含短语“我们的公司太棒了”
  • 单击特定的锚点
  • 检查特定div中的文本是否包含短语“单击此处联系我们”

这个插件既可以记录你在浏览器中的行为,也可以用来一点一点地构建这些测试。记录对于了解动作是如何构建的以及它们是如何工作的特别有用。这些操作存储在一个“测试用例”中,并且您可以在一个“测试套件”中拥有多个测试用例这些测试可以从 IDE 中运行,每个操作将分别变成绿色或红色,这取决于它是通过(成功执行或为真)还是失败。

images

图 10-20。硒 IDE

从 CSS 开发人员的角度来看,Selenium 提供的最有用的命令是 captureEntirePageScreenshot,它确实做了它所说的事情。使用这个命令,很容易在 Firefox 中自动创建一系列截图。

然而,只测试 Firefox 不如测试我们整个支持的浏览器矩阵有用。为此,Selenium IDE 的老大哥 Selenium RC 可用于针对多种浏览器、分辨率、色深和操作系统运行这些测试。Selenium RC 还支持比 Selenium IDE 更多的命令;例如,captureScreenshot 仅捕获当前视口中的视图。测试可能会很快变得非常大,并且完成起来很慢,如果您在持续集成(CI)环境中使用它们,这可能是一个问题(参见第九章),但是这可以通过 Selenium Grid 来克服,它允许您将许多服务器连接在一起并并行运行所有测试。

一旦您创建了您的截图并批准了它们,就可以将批准的版本与后续的截图进行比较,并基于此通过或未通过测试——但正如我们之前提到的,这可能是一个脆弱的测试。

尽管 Selenium IDE 感觉笨重且功能有限,但可用的工具套件是免费的、成熟的且非常强大。如果您需要所提供的其他功能(您很可能需要),Selenium 是帮助您实现这一目标的一个很好的工具。你可以在[seleniumhq.org/](http://seleniumhq.org/)了解更多信息。

尽管 Selenium 是您可能找到的最接近行业标准的集成测试工具,但仍有几种替代工具,下面列出了其中一些:

  • watir:??]
  • sahi:??]
  • 风车:[www.getwindmill.com/](http://www.getwindmill.com/)
浏览器主机

假设你唯一感兴趣的是截图, Browsershots 是一个简单得多的一次性测试。通过浏览到[browsershots.org/](http://browsershots.org/),您可以输入一个 URL,勾选您想要测试的浏览器和操作系统,然后提交表单。您的请求将被放入一个队列中,当完成时,您将返回一系列屏幕截图。Browsershots 可能需要很长时间才能完成,但它是免费的。或者,每月大约 30 美元,你可以获得优先处理,跳过许多排队。虽然它是一次性执行大量测试的好工具,但是它有几个缺点:

  • 它是托管的,这意味着您测试的页面必须对外部世界是可访问的,这对于您的组织来说可能是不可接受的
  • 试图使这一过程自动化是非常困难和脆弱的
  • 它非常慢

如果你别无选择,Browsershots 是一个有用的工具。

浏览器摄像头

Browsercam ( [www.browsercam.com/](http://www.browsercam.com/))是 Browsershots 的一个更有能力和功能的等价物,也有能力进行收费的交互。它还提供远程访问服务,以便您可以选择您想要的操作系统和浏览器,并直接连接到具有该配置的机器上进行尝试。从简单地生成截图的角度来看,它与 Browsershots 有相同的缺点,也很昂贵,尽管它更快。

总结

本章展示了许多工具中的一些,这些工具可以帮助您进行调试,以及了解如何决定测试哪些浏览器以及运行这些浏览器的最佳方式。它还向您展示了自动化可以帮助测试 CSS 的一些最佳方式。

在下一章,我们将把我们所有的知识付诸实践,我们将提供关于如何构建你自己的 CSS 框架的指南和例子。

十一、创建您的 CSS

在本章和附录中,我们把前面所有的章节付诸实践。我们打算把你在整本书中学到的一切都拿出来,给你一些现实生活中的例子,你可以根据自己的需要进行修改。虽然我们不打算规定您的流程以及您应该如何建立自己的指导原则,但我们会为您提供模板,供您在团队内部讨论时使用,以建立自己的指导原则。我们也将从头到尾经历制作一个框架的过程,然后你可以适应你的网站的需要。

在附录 1、2、3 和 4 中,您将分别找到示例 CSS 编码标准文档、示例可访问性指南文档、示例浏览器支持指南文档和示例开发流程文档。

在本章中,您将学习以下内容:

  • 如何将设计解构为网站的资产和模板
  • 创建 CSS 框架时要考虑哪些因素
  • 如何记录设计资产的 CSS 库

Igloo 冰箱零件公司网站

让我们向您介绍虚构的冰屋冰箱零件公司的设计。这里向您展示了设计团队提供的主页(见图 11-1 )和内部顶层部分页面(见图 11-2 )。

images

图 11-1。冰屋冰箱配件网站首页设计

images

图 11-2。冰屋冰箱零件网站的示例内容页面

设计团队努力创造符合业务需求和目标的设计,确保用户体验和交互尽可能清晰简单,信息传达顺畅,用户愉快地完成旅程。

正如在大型组织中经常发生的那样,许多利益相关者可能对设计应该如何,或者网站的某些区域应该包括什么内容有发言权。知道了几个有着不同职责和目标的人对这个结果的影响,CSS 作者和开发人员的工作通常是确保所创建的东西是可行的,尽可能干净地编码,并且所有的东西都被构建成最终产品不仅是可用的和可访问的,而且是可维护的。

下一步是分析这个设计,看看是否有任何问题需要我们在继续构建它之前解决。

分析设计

当在构建设计之前分析它时,有一些重要的问题需要回答:

  • 在不同的浏览器中出现不同的外观是可以接受的(和不可以接受的)是什么?
  • 在实现解决方案时,您可能会遇到什么问题?
  • 你还需要和设计师讨论哪些问题,如果不解决这些问题,它们可能会成为更大的问题?

回答完这些问题后,一个很好的练习是确定页面中使用的公共元素(如果有设计库,这可能已经由设计团队完成了;你可以在第五章中阅读更多关于设计库的内容。这将使您更容易避免代码中的冗余:如果您注意到一个组件在页面的不同位置被重复使用,您将知道您不应该使它的 CSS 过于具体,以便您可以允许它被重用。如果你注意到另一个组件有两个(需要的)变体,你会知道你应该为组件的默认状态创建基本 CSS,然后可以很容易地用另一个类扩展(受面向对象 CSS (OOCSS)的启发,你可以在第四章中读到更多)。

对于第一个问题,在与设计者讨论后,你被告知跨浏览器的不同外观是不可接受的。

至于问题的后半部分,以下元素跨浏览器看起来不同是可以接受的(但对于具有 A 级支持的浏览器,要尽可能相似):

  • 盒子上的圆角
  • 在按钮上投射阴影
  • 在文本上投射阴影

您的团队针对该解决方案提出的一些问题和疑问如下:

  • 如何处理主页横幅上的文本?它使用的是 Futura Condensed ,这不是一种网络安全字体,只能通过font-face通过网络字体服务获得(更多细节见第五章)。
  • 该设计没有为导航项目或链接提供悬停状态。你需要将这些反馈给设计团队,这样你们就能一起找到解决方案。

您应该在自己的团队中提出的一个问题的例子是,使用高级选择器(如:nth-child()选择器)删除第一个/最后一个元素上的边距和边框是否可以接受。高级选择器带来的性能提升值得吗?你应该选择通过一个类来定位这些元素的更简单的解决方案吗?如果你使用高级选择器,你会为不支持它们的浏览器提供 JavaScript 后备吗?

最后一个问题也应该咨询设计团队,因为如果你不提供后备,一些浏览器会显示不同的设计。请记住,不仅要向开发人员,还要向业务部门解释提议的解决方案的利弊,因为设计人员更希望实现的解决方案尽可能与他们的原始作品相似,而不管您是否需要更多的工作来实现它。

网格

设计的底层网格应该在设计指南中指定,而不是前端开发人员通常会创建的东西。意识到这个网格,至少在基础层面上,将会使构建 CSS 更加容易,并且会使你的结果更加准确。网格在第五章中有更详细的介绍。

在我们的示例网站中,网格基于 Blueprint 默认网格,该网格由 24 列组成,宽度为 30 像素,间距为 10 像素(参见图 11-3 )。了解了这一点,您就会知道如果一个元素跨越 4 列,那么它的宽度将是 150 像素(这个结果是通过将 4 * 30px 列的宽度加上 3 * 10px 间距的宽度得到的)。

images

图 11-3。冰屋冰箱部件设计,底层网格可见

可复用组件

如前所述,在开始编写标记和 CSS 之前,识别可重用的块是很重要的。如果没有可以遵循的设计库或组件库,这一点尤其重要。

在提供给您的两个页面中,您已经确定了七个不同的框或内容块,它们将在网站的各个不同页面中重复使用(参见图 11-4 ):

  • 圆形苍白色(“接触”)
  • 带背景图像的圆形(“关于”)
  • 带链接列表的渐变(“服务”)
  • 行动号召横幅(“预约工程师”)
  • 搜索
  • 桌子
  • 带 3 列的苍白色(“按”)

images

图 11-4。不同类型的箱子。

这个列表给你一个设计的鸟瞰图和一个重要的轮廓,供你开始工作时使用。它还使得检测非常相似的块和元素变得容易,并且可以合并到单个模块中,因此 CSS 和设计可以进一步简化。在我们的例子中,在内容页面上(参见图 11-2 ,你可以看到第一排服务框和第二排非常相似,除了第二排的标题更大(参见图 11-5 )。

images

图 11-5。左边方框和右边方框的唯一区别是标题的大小

理想情况下,通过统一标题大小使其成为一个单一框的决定不是由实现团队做出的,而是必须由设计师签字同意。尽管如此,重要的是要意识到这些微小的变化可能会渗入设计中,并使它们变得更加复杂(设计团队和品牌经理也将把它们作为他们角色的一部分)。您的案例中的变化不太可能像前面的例子那样简单,但是您必须意识到,有时设计解决方案可以而且应该被改变,以保持设计和代码的简单。

调色板

该设计为我们呈现了十种经常使用的颜色:

  • 正文
  • 帮助文本(搜索)
  • 未访问的链接
  • 导航背景
  • 选择导航
  • 方框背景(蓝色和黄色)
  • 虚线
  • 渐变框边框
  • 表格背景

还使用了四种渐变:链接列表框、按钮、表格标题和页脚背景。

也使用其他颜色(例如,在奖项宣传横幅上),但它们主要是一次性的,不应该扰乱基本的调色板。

在分析所使用的颜色时,通过直接与设计团队交谈或参考贵组织的设计指南,确保您有正确的必要值。重要的是,例如,每当你处理简单的文本时,你使用相同的正确颜色,而不是以 12 种不同的非常相似(和不正确)的变化结束。

无障碍问题

理想情况下,设计师或设计团队会对设计进行测试,以便颜色组合为色盲用户提供足够的对比度。在可访问性指南(参见附录 2)中,建议使用诸如 Color Oracle 之类的工具来测试设计,这提供了一种快速简单的方法来模拟色盲用户将会看到的内容。

前景色和背景色也应该使用对比检查器进行测试,比如 Jonathan Snook 的颜色对比检查(同样,在可访问性指南中也提到了)。

对设计的快速分析发现了一个问题:页脚文本和背景之间的对比度似乎不够。在这种情况下,如果怀疑被证明是正确的,你可以与设计团队讨论最佳解决方案:也许简单地将文本颜色改为白色就可以解决问题。

目前,我们只是根据手头的设计来分析可能的复杂情况。在开发此设计解决方案时,应该已经讨论了其他可访问性考虑事项,而其他考虑事项只能在创建代码时解决。

如果存在可以立即发现的可访问性问题,最好在动手使用代码之前解决它们。

沟通很重要

开发人员很容易感觉与设计人员完全分离,交流也很少。这并不理想。设计师和开发人员都在朝着同一个目标努力:拥有一个伟大的网站,人们会觉得必须访问和使用,这将有助于公司实现其目标。虽然开发人员可能不愿意为设计师提供设计建议,但是有理由要求一个勤奋的 CSS 作者不仅反馈、建议甚至教育设计师,还包括所有参与网站创建的人,让他们了解最佳实践、标准以及以某种方式做事的含义。这不是一条单行道:同样勤奋的开发人员应该对业务的其他部分带来的新想法持开放态度,并应该努力找到问题的解决方案,这些问题可能不一定容易在代码中解决,但最终对用户来说是最好的。

一种容易引起混淆的元素是标题,所以它们是一个很好的例子,说明设计者和页面构建者之间的沟通是多么重要。如果设计者牢记在特定情况下使用的标题级别不取决于我们想要的字体大小,而是取决于内容的实际层次,那么他将更容易开发一个解决方案,其中标题(以及它们的大小和相对于它们在页面上的位置的比例)被更好地考虑和更仔细地考虑。

创建 CSS

在分析整体设计的重要步骤之后,是时候开始为网站创建 HTML 和 CSS 了。由于本书关注的是事物的 CSS 方面,我们假设你将遵循 web 标准,使你的标记可访问,并且(尽可能)干净和有语义。请记住:干净高效的 CSS 只能来自干净高效的标记,尽管只有在困难的时刻,需要解决更复杂和混乱的难题时,真正的专业知识才会显露出来。如果你在[procssforhightrafficwebsites.com](http://procssforhightrafficwebsites.com)前往这本书的配套网站,你可以随意查看它的标记。

我们将继续使用冰屋冰箱零件公司的网站作为我们的例子。即使它不是一个过于复杂的网站,我们也会为你提供一些提示,告诉你如何思考和编写可以应用于任何网站和过程的 CSS。

这个练习的主要目标是讨论和修改设计;为您和您的团队提供 CSS,使您无需编辑 CSS 文件即可创建更多页面(除非需要向库中添加不同类型的设计元素);并向您展示如何创建高效且可维护的代码,以及如何创建和扩充相关的设计库。

Igloo 冰箱配件公司网站的 CSS 创建遵循该组织的 CSS 标准指南(附录 1 )、CSS 可访问性指南(附录 2 )、浏览器支持指南(附录 3 )和开发流程(附录 4 )。

我们将以我们的字符集声明开始每个 CSS 文件:

@charset "UTF-8";

这有助于我们避免以后必须对字符进行转义,从而节省字符并使我们的代码更容易阅读。

正在评论

遵循 CSS 标准指南(参见附录 1),我们将在开头使用 CSSDOC 注释(参见第二章了解关于 CSSDOC 的更多细节)——文件注释——并用于分隔样式表的各个部分——部分注释。在文件注释中,我们将包括样式表的描述和关于项目、最初创建样式表的团队和版权的信息。我们还将使用该注释来包含整个 CSS 中使用的主色列表(如前面所列)。下面是这个评论的样子:

/**  * Igloo Refrigerator Parts Inc Corporate site  *  * Main CSS file for the IRP Inc's corporate site  *  * @project             IRP Inc Corporate  * @author               Design Team at IRP Inc  * @copyright            2011 Igloo Refrigerator Parts Inc  *  * @colordef            #333; main text  * @colordef            #999; help text (search)  * @colordef            #1299b4; unvisited links  * @colordef            #cdf2f8; navigation background  * @colordef            rgba(18,153,180,.2); navigation selected  * @colordef            #a1e0e9; navigation selected fallback  * @colordef            #e1f4f7; box background blue  * @colordef            #fffbeb; box background yellow  * @colordef            #cecac5; dotted lines  * @colordef            #b0daea; gradient box border  * @colordef            #fffbeb; table background  */

在文件注释之后,我们将声明该文件是否有任何依赖关系。在我们的例子中,我们将导入一个重置样式表(reset.css ),因此我们将包含以下注释:

/**  * Dependencies  *  * Importing reset file: reset.css  */

后面是目录,以便更容易地引用样式表的内容。请记住,如果这个目录比较简洁,那么它会更实用,所以不必经常更新。

/**  * Table of contents  *  * Structure  * Generic typography and lists  * Global elements  * Links  * Forms  * Tables  * Visual media  * Components  * Reusable  * One-offs  */

这个结构也表明了我们需要在样式表中包含哪些部分的注释。不过,最好已经有一个基本模板,它应该包括一个示例文件注释和 CSS 文件的理想结构,后面是部分注释。这将使开发人员更容易开始创建新的样式表。以下是部分注释的示例,其中描述是可选的:

/**  * Reusable  *  * Modular classes that can be applied to other elements  *  * @section reusable  */

除了介绍性和分段注释之外,您可以添加到样式表中的一些最有用的注释是那些解释为什么采取某个操作的注释。在我们的例子中,我们对使用的每一个 Internet Explorer 过滤器都做了注释,以便文件的未来编辑者知道使用它们的原因。这里有一个例子:

#masthead {         margin-top: -20px;         *margin-top: 0; /* IE 7 and below: Navigation is partially hidden on IE 7 without this property. */ `padding-top: 20px;

}`

在这种情况下,我们需要反转“masthead”部分的负上边距属性,这样它的内容在 IE 7 中就不会被裁剪。通过在属性名前添加星号,只有 IE 7 及以下版本才会正确解析第二个属性(你可以在第三章中读到更多关于这个的内容)。

您可能希望在 CSS 标准指南的主样式表中包含一个注释,说明哪些 CSS 技巧是可以接受的。有时,当应用的 hacks 数量很少时,不将这些污点从主 CSS 中分离出来会更有效率和性能。你可能会说,在未来,如果你想删除这些黑客,这将使它成为一个更困难的任务,但如果不动这些简单的记录黑客不太可能会造成任何问题。

你也可以(事实上,你应该)用一种共同的语言来标记这些黑客,以便将来容易搜索。我们将使用“HACK_”以及 HACK 的详细信息。我们更新的代码如下:

`#masthead {
        margin-top: -20px;
        margin-top: 0; / HACK_IE_LTE_7: Navigation is partially hidden on IE 7 without this
property. */
padding-top: 20px;

}`

这段代码的确切语法在附录 3 中有详细说明,你可以在第二章中阅读更多关于注释的内容。

单位

在我们的 CSS 中,我们将使用相对和绝对单位的组合:ems 用于字体大小,像素用于其他测量。这一决定背后的原因是,在大多数浏览器中,以相对单位调整大小的字体可以调整大小,但是使用 ems 作为元素宽度等值会使整个布局扩展,即使用户已经设置了仅调整文本大小的首选项。

为了简化计算,我们在body元素中包含了属性font-size: 62.5%。这将使默认浏览器字体大小为 10 像素(通常是 16 像素)。知道了这些,我们就可以推断出 1 em 等于 10 像素,1.1 em 等于 11 像素,以此类推。

基线样式

我们希望从头开始,所以我们将创建一个重置样式表(您可能希望将其称为“base.css”而不是“reset.css”),通过重置一些不太一致的属性(如边距和填充),使大多数 HTML 元素在跨浏览器时工作类似。但是我们不想重置一切,所以我们将利用一些用户代理样式表的缺省值,而不是在以后将它们添加到我们的代码中(你可以在第三章中阅读更多关于用户代理样式表的内容)。

从 Eric Meyer 的 reset 样式表开始(你可以在第四章的中读到更多关于这个和其他 reset 样式表的内容),我们将从初始声明中删除以下元素:fieldsetdtdd。尽管这些元素的边距和填充在一些浏览器中不一致,但它们作为基本样式已经足够好了,我们知道,在这种情况下,它们不会经常使用。

下一步是定制body选择器,使其满足我们的需求。在他最初的重置中,Eric 只对body元素应用了line-height: 1。在我们的案例中,我们需要的不仅仅是这些:

body {         background: #fbfdfc url(i/bkg.jpg);         color: #333;         font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;         font-size: 62.5%;         line-height: 1; }

我们还将为blockquoteinsabbr等元素添加基本样式。我们将把默认的版式留给主样式表,此时只对body元素应用通用的字体相关属性。

结构

我们网站的设计遵循一个简单的两栏布局,顶部是全宽的页眉部分(包含导航),底部是全宽的页脚。如前所述,底层网格(参见图 11-3 )基于 24 列布局,每列 30 像素宽,由 10 像素宽的间距隔开。容器元素可以跨越 6、9、12、18 和 24 列。

在开始构建您的框架时,记下这些度量是相当简单但极其重要的一步。这些值会在你的样式表中反复出现,所以拥有正确的值而不仅仅是近似值是至关重要的。

这同样适用于边距和填充等值。在我们的设计中,标准盒子的填充值为 14px 11px。优秀的设计师不太可能创造出这样的解决方案,例如,列表项呈现不同的底部边距。一致性很重要,模型中的一致性必须转化为代码中的一致性。

让我们开始写我们的 CSS。在我们的基本模板的“结构”部分,我们将添加主要内容包装器的样式:

.wrapper {         width: 950px;         margin: auto; }

我们在这个实例中使用了一个类,因为我们将在每页中不止一次地使用这个包装器。因为我们设计的一些区域有全幅背景,内容区域居中,我们将使用这个泛型类来设计内部容器divsection元素的样式,同时保持主区域包装器全幅。下面是页脚标记:

`


        

                

                            
  • Help

  •                         
  • Terms of Service

  •                         
  • Privacy Policy

  •                 

                

© 2011 Igloo Refrigerator Parts Inc


        

`

在这个泛型类之后,我们将为其他主容器添加样式,比如顶部header元素、主(#main和次列(#sec区域,以及footer元素。然而,我们将把#main#sec选择器的宽度留到以后…

因为我们希望我们的元素尽可能灵活,我们将创建可以附加到任何容器的附件类,给它一个我们希望允许页面生成器使用的可能宽度。对于这些,我们将使用表示类。只要有正当的理由,这是可以的;在我们的例子中,除了“span-6”或“col-6”之外,我们如何命名一个表示跨越 6 列的元素的类呢?如果我们包含了每个元素的宽度(例如,“main”和“sec”容器的宽度,等等),我们就不能为页面创建者(无论是开发人员还是内容编辑人员)快速创建新页面提供足够的灵活性。如果这确实是您需要实现的,那么我们建议您不要使用这些无意义的类名——这是为了更高的灵活性我们愿意接受的一种折衷。以下是我们将为集装箱宽度提供的类别:

.span-6, .span-9, .span-12, .span-18 {         margin-right: 10px; } .span-6 {         width: 230px; } .span-9 {         width: 350px; } .span-12 {         width: 470px; } .span-18 {         width: 710px; }

这些类遵循了已知框架设置的例子,其中每个宽度选择器还包括一个右边距。当你需要浮动容器的时候,你可以使用类“fRight”或者“fLeft”(后面会提到)。要删除一行中最后一个容器的页边空白,您必须将类“last”添加到该容器中,该类只包含属性margin-right: 0(您可以在本章后面阅读更多关于可重用类的内容)。

当构建一个通用的公共框架时,比如 960.gs 或 Blueprint(你可以在第四章中读到更多关于这些框架的内容),你需要确保你的代码能适应尽可能多的变化。然而,在创建定制框架时,没有必要走极端。你知道它的用途,你知道可能的变化,你甚至可能知道你不希望它做什么。您可以将该框架视为一种工具,它允许您根据需要创建任意多的页面变体,不多也不少。这个框架不是每个人的一切,而是必须的,这意味着没有浪费或未使用的代码或带宽。

对于我们的网站,我们不需要超过前面列出的容器宽度。在您的特殊情况下,您可能想要更复杂的变化。

排版默认值

在定义了主要的结构元素并为不同的布局创建了可重用的类之后,我们将为我们需要迎合的所有元素添加基本的印刷样式。这与 reset 样式表的区别在于,在这一部分中,只包括具有字体相关属性的选择器。

通过为每个元素设置一个基本样式,您可以确保内容创建者能够创建新的页面,这些页面将始终呈现至少正确的元素层次结构,并遵循设计准则。我们将对所有六个标题级别(h1h6)、段落(p)、列表元素(li)、定义列表(dtdd)、引号(blockquote)和预格式化文本(pre)进行样式化。该设计具有非常一致的标题层次结构,因此我们知道,即使某些级别标题位于特定的容器中,它们也会具有相同的font-sizeline-heightmargin值。有一些变化,但是它们不应该在默认样式部分中处理,而应该只在组件级别添加,只应用必要的更改并利用继承来使变化最小化并避免冗余。

正如我们之前提到的,我们将使用 ems 来定义网站中的font-size值,但是我们将使用像素来定义高度、宽度、边距和填充。这将允许字体大小调整,甚至在旧的浏览器,保持布局一致,并避免不必要的水平滚动条。

以下是前三级标题的样式:

h1, h2, h3, h4, h5, h6 {         font-weight: normal; } h1 {         font-size: 2.4em;         margin-bottom: 10px; } h2 {         font-size: 1.8em;         margin-bottom: 12px; } h3 {         font-size: 1.5em;         margin-bottom: 10px; }

下面是我们的段落和列表元素的样式:

p, li, dt, dd {         font-size: 1.3em;         line-height: 1.4;         margin-bottom: 12px; }

line-height值是无单位的,因此它可以适应文本的大小。在这种情况下,计算出的font-size值将是13px,计算出的line-height值将是18.2px(你可以在第三章中了解关于计算值的更多信息)。如果字体大小增加,例如增加到1.5em,计算值将分别为15px21pxline-height值与font-size成比例增加。

我们有意识地将uloldl元素排除在基本样式之外:默认的用户代理样式满足设计的需要,因此没有必要重置或覆盖这些样式。

您可以更进一步,为某些元素后面的标题添加样式。例如:

p + h1, p + h2, p + h3, p + h4, p + h5, p + h6 {         margin-top: 24px; }

可能性是无限的:您可以为遵循特定列表类型或图像的标题、遵循相同元素的段落、遵循特定标题级别的列表类型等包含样式。

对于我们的网站,我们还将为放置在blockquote元素中的pcite元素添加样式:

blockquote p {         font-size: 1.3em;         font-style: italic;         margin-bottom: 8px; } blockquote p:before {         content: "“"; } blockquote p:after {         content: "”"; } blockquote cite {         display: block;         font-size: 1.3em; } blockquote cite:before {         content: "—"; }

前面的代码应该是不言自明的。我们已经使用了:before:after伪元素将引号添加到 blockquote 中段落的开头和结尾。因为我们已经声明文件的字符集是 UTF-8,所以没有必要对这些特殊字符进行转义。我们没有使用这些伪元素的 CSS3 版本(分别是::before::after,以便 Internet Explorer 8 能够理解它们:我们希望向尽可能多的浏览器显示相同的设计。

出于两个原因,我们在本节中没有包括表单和表格样式。我们的表单元素将从用户代理样式表中继承大部分样式,只添加一些用于定位和定制按钮设计的样式。表格样式本身就很复杂,所以在样式表中保留一个单独的部分,将表格作为一种特殊类型的组件,可以使这个基本样式部分更清晰,更容易浏览和引用。

你可以在第五章中阅读更多关于排版考虑的内容。

全球元素

添加基本样式后,我们将为全局元素创建样式。这些元素出现在网站的每个(或者几乎每个)页面上。在我们的例子中,它们是标题区域、主导航和页脚。对于某些应用程序,我们需要覆盖我们已经创建的一些基本样式,例如导航列表:

`nav ul {
        list-style: none;

}
nav li {
        display: inline;
        font-size: 1.4em;
        font-weight: bold;
        margin-top: -35px;
        margin-bottom: 0;
}`

对于主导航中的列表项,我们必须覆盖默认的用户代理list-style值,应用不同于基本样式的边距值,并增加字体大小。我们将把链接样式留给“链接”部分,这样我们就有了一个集中的区域,可以确保所有的链接状态都得到满足。您不必在自己的样式表中遵循这种结构,但是我们的经验表明,将所有链接放在同一个位置可以使不同的开发人员在需要编辑或扩充时更容易在文档中找到它们。

根据手头的设计,这一部分可能需要包含更多或更少的元素。您可能会发现将全局元素分隔在各自的部分中更容易,然后将不同的部分用于主导航、辅助导航、页脚等等。

组件和可重用类

在我们的例子中,我们将组件分类为应用了特定样式的可重用内容块。通过将组件的类或 ID 添加到容器元素中,可以使其中的元素继承这些样式。这使得 CSS 更加可重用和模块化。这些块不是为它们出现的每个实例设计样式,而是与位置无关的、可移动的块。我们将使用 serviceBox 作为我们的示例组件(参见图 11-6 )。

但是,在创建 serviceBox 之前,我们首先创建了一个基本的 Box 组件:

.box {         background: #fffbeb;         padding: 14px 11px;         -moz-border-radius: 4px;         -webkit-border-radius: 4px;         border-radius: 4px; }

这个类可以应用于任何容器,它将使用正确的圆角值、填充和背景来设置样式。通过“serviceBox”类,我们将扩展“Box”类。这遵循了 OOCSS 的概念,其中类可以被其他类重用和扩展,从而形成更加模块化的样式表。你可以在第四章中阅读更多关于 OOCSS 的内容。

下一步是创建 serviceBox 类:

.serviceBox {         background: #fff url(i/box-grad.jpg) repeat-x left bottom;         border: 1px solid #b0daea;         overflow: hidden;         height: 1%; }

如设计所示,serviceBox 有一个不同的背景、一个边框,并以三行显示。“serviceBox”元素的宽度和边距将由父容器div决定,并应用正确的“span”类(在本例中为span-6)。我们包含了值为 1%的height属性,这样 IE 6 将清除容器,因为它不理解overflow: hidden属性(在其他更兼容的浏览器中用于清除容器)。这个框中的内容总是标题、段落和链接列表。了解了这一点,我们可以进一步设计其中的内容:

.serviceBox ul {         list-style: none;         margin-left: 0; } .serviceBox li {         border-top: 1px solid #eef9fb;         margin: 0;         width: 100%; }

我们已经利用了这个事实,即标题使用的样式与我们文件的基本样式部分中定义的样式相同,所以我们不需要在这里为它添加任何样式。我们现在只需要给列表元素添加圆角。因为这种样式不会随着链接的悬停或活动状态而改变,所以我们可以在本节中添加它:

.serviceBox li:first-child a {         border-top: none;         -moz-border-radius: 4px 4px 0 0;         -webkit-border-top-left-radius: 4px;         -webkit-border-top-right-radius: 4px;         border-radius: 4px 4px 0 0; } .serviceBox li:last-child a {         -moz-border-radius: 0 0 4px 4px;         -webkit-border-bottom-left-radius: 4px;         -webkit-border-bottom-right-radius: 4px;         border-radius: 0 0 4px 4px; }

请注意,WebKit 特定的属性是为 WebKit 的旧版本单独添加的。WebKit 只支持在最新版本的border-radius属性中的简写。一些老的浏览器看不到这种风格,但是他们也不会理解border-radius属性,所以这是一个公平的妥协。我们已经设计了我们的 serviceBox 组件!如前所述,我们将在样式表的适当部分添加链接样式。

组件是可重用的,但是这里我们对组件和可重用类进行了区分,因为可重用类更具有表示性,并且倾向于只满足单个属性/值的组合。创建组件主要是为了保存特定类型或组合的内容。

在我们的主样式表中,我们已经创建了五个不同的可重用类。第一个可重用的类可能是大多数前端开发人员所熟悉的:clearfix。这个类可以应用于任何需要“自我清除”的元素(清除时没有专门为此创建的元素)。我们还包括 Internet Explorer 7 和以下版本的这个小技巧:

.clearfix:after {         content: ".";         display: block;         height: 0;         clear: both;         visibility: hidden; } .clearfix { /* HACK_IE_LTE_7: IE 7 and below don't support the :after pseudo-element (above). */         *zoom: 1; }

接下来,我们创建两个类,允许开发人员和内容创建者轻松地左对齐或右对齐元素:

.fLeft {         float: left; } .fRight {         float: right; }

接下来,我们将添加可以应用于一行中最后一个元素的“last”类。这将删除右边距,否则将被应用,以便它整齐地适合可用的宽度:

.last {         margin-right: 0; }

最后,我们包括一个可访问性类,允许内容创建者隐藏只有屏幕阅读器才能阅读的文本:

.accessibility {         position: absolute;         text-indent: -9999px; }

一次性的

总会有一些元素需要被创建和设计,这些元素并不遵循我们添加到框架中的任何东西的设计。我们称这些为一次性的。您可能更喜欢称它们为“异常”、“唯一的”,或者任何您认为合适的名称。

在 Igloo 冰箱零件公司网站的例子中,我们已经确定了(到目前为止)两个特例:主页上的横幅和“最佳工程服务奖”框,也在主页上。

对于横幅,我们将创建一个可以附加到#masthead组件的类,包括正确的背景图像和文本大小:

.planet {         background:url(i/hp-banner01.jpg) no-repeat center 20px; } .planet h2 {         font-size: 4.8em;         margin-bottom: 0; } .planet p {         font-size: 1.6em;         padding-bottom: 6px; }

这个类基本上是#mastead ID 的扩展,并依赖于它。因为它的风格非常特别,并且不适用其他任何东西,所以它被放在样式表的“一次性”部分下。

对于奖励框,我们也将利用一些元素的默认样式,但是我们需要改变许多属性:

`#bestEngAward {
        border: 1px solid #fce8b8;
        background: #fff;
        padding: 23px 20px;
}

bestEngAward h2 {

color: #fcb61a;
        font: 3em/1.1 Impact, "Helvetica Compressed", "Helvetica Inserat", Arial Narrow,
Tahoma, Geneva, "MS Sans Serif", sans-serif;
        margin-bottom: 20px;
        text-transform: uppercase;
}

bestEngAward p {

margin-bottom: 0;
        width: 100%;
}

bestEngAward p a:link, #bestEngAward p a:visited {

border: none;
}

bestEngAward p a:after {

content: " ›";
}

bestEngAward blockquote {

background: url(i/blockquote.gif) no-repeat;
        color: #0f6080;
        margin: 0 25px 0 0;
        padding: 3px 0 0 40px;
        width: 321px;
}

bestEngAward blockquote p {

font-size: 1.6em;
        font-style: normal;
        margin-bottom: 3px;
} #bestEngAward blockquote p:before {
        content: "";
}`

为了改变它们的样式,我们必须覆盖h2p元素的属性、链接样式以及blockquote元素及其子元素的样式。如果最初的样式过于具体,这段代码会更加冗长:我们要么没有利用继承来声明最少的属性,要么不得不重写过于具体的选择器。

小心:随着网站的增长,一次性网站的数量可能会呈指数级增长。设计者应该对这种情况敏感,并确保没有创建与现有元素非常相似的元素。这不仅会使 CSS 变得更加复杂和不确定,而且会使设计方案更加淡化。确保您正在创建的元素还没有以某种方式出现在您的样式表中,或者它不能扩展具有几个属性的现有组件。

妥协

作为有意识的 CSS 作者,我们知道什么是最佳实践;我们了解并遵守网络标准;我们掌握了最新的 CSS 技术,这些技术通常是为了让 CSS 更有效,让开发人员更有效率而开发的。这些我们都知道。但是作为在大型网站上工作的 CSS 作者——数百万用户在无数不同的设备和设置上访问——我们也知道我们需要做出一些妥协。

冰屋冰箱零件公司网站也不例外。我们已经提到了一些妥协,比如使用无意义的类名来增加灵活性,或者求助于用户代理样式表的默认设置来实现一些元素的基本样式。

另一个我们妥协的例子是使用一个图片来在主页的横幅上显示一个自定义的非网页安全字体。最初的设计使用 Futura Condensed Medium,很少有用户会在他们的计算机中安装这种介质。这种情况的选项如下:

  • 使用图像
  • 使用图像替换技术
  • 使用库芬或 sIFR(你可以在第五章中了解更多)
  • 使用 Fontdeck 作为 web 字体服务,因为它提供了这种字体

由于这种字体只在一种情况下使用,简单地用图像替换文本并使用alt属性来识别图像中的文本被证明是最快、最简单、最便宜的解决方案,并且不会妨碍可访问性。如果更多地方需要这种字体,我们最喜欢的解决方案是利用像 Fontdeck 这样的服务,但是额外的下载和增加的费用在这种情况下是不合理的。

以“最佳工程服务奖”框中使用的字体为例,影响,我们知道这是 Windows 和 Mac 电脑中默认普遍安装的字体。在这种情况下,最好的解决方案是创建一个字体堆栈,在没有安装字体的地方,堆栈中的第二、第三甚至第五种字体仍然是可接受的(最终的字体堆栈是Impact, Helvetica Compressed, Helvetica Inserat, Arial Narrow, Tahoma, Geneva, MS Sans Serif,sans-serif)。参见图 11-6 。

images

图 11-6。以首选字体 Impact(顶部)呈现的文本与以字体堆栈中稍后出现的字体呈现的文本之间的比较,在本例中为 Arial Narrow(底部)

这些都是非常简单的例子,说明您可能必须做出妥协,但只要您权衡所有选项,并选择在效率和维护方面最可行的一个,您就应该在正确的轨道上。请记住,您(以及参与设计创建的团队)可能是唯一知道事情看起来不完全正确的人;用户不会在不同的平台上比较网站的表现。

跨浏览器一致性

跨浏览器一致性的主题与前面讨论的妥协主题紧密相关。当我们在开始 CSS 之前分析设计时,我们被告知需要完美跨浏览器的唯一元素是公司的徽标。其他元素在具有 A 级支持的浏览器中应该尽可能看起来相同(浏览器支持指南见附录 3)。

为了确保徽标即使在不支持 alpha 透明 PNG 图像的 IE 6 中看起来也是一致的,我们求助于 Fireworks hack,在那里我们将带有 alpha 透明的徽标导出为 8 位文件;这给了我们支持它的浏览器中的 alpha 透明性和 IE 6 中的索引透明性。这种差异几乎无法察觉,所以被认为是可以接受的。你可以在第八章中读到更多关于这个技巧的内容。

header h1 a:link, header h1 a:visited {         background: url(i/logo.png) no-repeat;         border: none;         width: 132px;         height: 46px;         display: block;         text-indent: -9999px; }

我们可以用下面的代码达到同样的效果,使用一个不同的图像和一个黑客来让我们对 IE 6 的显示有更多的控制:

header h1 a:link, header h1 a:visited {         background: url(i/logo.png) no-repeat; **        _background: url(i/logo.gif) no-repeat; /* HACK_IE_6: IE 6 doesn't support alpha** **transparent PNGs */**         border: none;         width: 132px;         height: 46px;         display: block;         text-indent: -9999px; }

在这种情况下,这种级别的控制是不必要的,但我们已经求助于主样式表中的一些黑客(或过滤器)来克服其他浏览器的差异。我们知道你在想什么:你不应该使用黑客。在需要大量黑客的地方,我们当然不推荐这种技术。但是,当只需要几个文件时(对于整个网站来说),我们必须务实:我们处理的是高流量的网站,在这里 HTTP 请求需要最小化,并且只为特定的浏览器提供额外的文件从来都不是理想的。只要这是一个全球性的决定,并且开发者被告知他们可以使用哪些黑客技术,我们不认为这是一个你应该绝对忽视的方法。它对性能有积极的影响,并且使值在两种情况下更容易同时更新:符合和不符合。有多少次你忘记了更新 IE 专用样式表中背景图片的文件路径或者相应的min-height / height值?

CSS 中没有考虑到的(但被认为是可接受的变化)最明显的浏览器呈现差异出现在 Internet Explorer 6 和 7 中。

  • 主导航呈现不完美(即 6 和 7)(参见图 11-7 )
  • 不支持用于定位搜索输入的属性选择器,这会影响搜索框的样式(即 6 和 7)。参见图 11-8 。
  • 当不支持:empty伪类时,空的表格单元格呈现背景(即 6、7 和 8)。参见图 11-9 。

images

图 11-7。Safari 5(上图)和 Internet Explorer 6(下图)中呈现的主导航

images

图 11-8。Internet Explorer 7(右)中搜索框不显示背景图片(火狐 3.6,左)

images

图 11-9。火狐 3.6(上)正确渲染空表格单元格。Internet Explorer 8(底部)没有,因为它不理解 :empty 伪类

在某些浏览器中根本不会显示的其他装饰性细节如下:

  • 圆角
  • 主导航中所选链接的透明度
  • 在按钮上投射阴影

根据我们的经验,这些细节是大多数设计师乐于在不同浏览器中以不同方式呈现的。让这些技术在不兼容的旧浏览器中一致地工作所花费的时间以及这些技术对样式表(和标记)的可维护性的影响是不合理的,要知道有更简单的解决方案看起来仍然是正确的。

然而,我们采取了一些措施来为旧浏览器提供最好的解决方案:

  • 一些背景没有使用 CSS3 渐变,而是使用了简单的 GIF 图像。
  • 没有使用高级选择器来定位第一个和最后一个元素,否则会破坏旧浏览器中的布局(由于缺乏支持),而是使用了类。
  • 在使用透明色的地方,提供了纯色的备用色,尽可能地模拟支持透明的情况下显示的结果。

有时我们需要避开更复杂和高级的功能,如果我们知道这样做会给更多的访问者提供更好的体验。我们不建议你走极端;正如你从上面的列表中所看到的,有更新更简单的方法来做一些在几年前需要几个小时或几天的事情。但有时,通过简单地使用图像而不是 CSS 渐变,对于一些用户来说,体验会成倍地提高,他们会感谢这种努力。

可访问性和链接

在一个网站的创建和开发过程中,易访问性必须是我们脑海中一直存在的东西。就前端开发而言,它必须从最基础的东西开始,即标记,赋予内容意义的东西,然后逐步发展到其他层——样式和行为。

Igloo 冰箱零件公司网站通过以下方式确保尽可能方便访问:

  • 确保网站内容清晰易懂,禁用所有样式,启用 CSS,禁用图片。
  • 确保前景色和背景色的组合对色盲用户有足够的对比度。
  • 测试网站的字体大小上下增加两步(参见图 11-10 )。
  • 不仅为键盘用户提供悬停和活动链接状态,还提供焦点链接状态。
  • 包括一个“跳转到内容”的链接,就在页面的顶部。这将使用户更容易直接进入内容。
  • body类和 id 的形式提供钩子,用户可以用它来创建用户样式表。
  • 只要有可能,就使用微格式,微格式可以将内容转换成更易于机器阅读的格式。这有助于屏幕阅读器,以及辅助设备和搜索引擎。
  • 包括一个合理的打印样式表。

images

图 11-10。尽管略欠优雅,但这种布局巧妙地保留了两级字体大小的增加。

其他步骤可以添加到您的列表中,例如确保网站的移动版本去除了次要元素(但仍为用户提供查看完整网站的链接),并确保所有图像文件的大小尽可能小。

所有这些措施都应该存在于 CSS 可访问性指南文档中(参见附录 4)。

我们将链接的主题包含在可访问性中,因为它们是使我们的网站可访问时要考虑的最重要的因素之一,并且经常被遗忘。在我们的样式表中,我们将所有的链接样式加入到一个单独的部分中,这样就更容易比较不同的链接样式,并保证所有必要的链接状态都是样式化的。

你可以在第六章中阅读更多关于可访问性的内容。

文档和设计库

一旦定义了可重用元素,下一步就是将它们保存在一个可以被设计者、开发者和内容编辑者访问的存储库中。

理想情况下,每个可重用的代码片段都应该被可视化地记录下来,并伴随着在页面上使用它所必需的 HTML 标记,以及 Photoshop 文件,以便设计人员可以轻松地进入他们的设计、示例、类名、id 和其他任何可能有用的内容。这个存储库应该对每个人都是可访问的,可搜索的,有版本控制的。

例如,如果我们要记录 serviceBox,显示创建它时使用的不同测量方法将会很有用(见图 11-11 )。

images

图 11-11。文档化的可重用模块。

当开发人员需要在页面中使用此框时,他们将复制并粘贴以下 HTML 标记:

`


        

Title


        

Description of title


        

`

在这种情况下,我们可以指定这个特定的块不应该被自己使用;它应该总是以三的倍数用于行中,其中每行的最后一个块应该包括“last”类(一个可重用的类,可用于删除一行中最后一个元素的右边距,不管上下文如何),并在前面加上一个标题(h2)。我们还可以指定每个块应该包含的副本数量,应该包含多少个链接,以及它是否依赖于周围的代码(在本例中不是)。

尽管我们的建议是面向开发人员的,但除了代码和规范之外,还包括可供设计人员使用的 Photoshop 文件(或其他类型的分层文件,如 Illustrator 或 Fireworks)也很有用。

请记住,如果不及时更新,这个库很容易变得无用。给某人分配定期更新的任务是非常重要的。

总结

最后,我们已经准备好了一个网站,它是用 CSS 主干构建的,可以容纳我们想要的任何页面。在这一章中,我们带你剖析了设计,以及你可能必须进行的对话,以确保设计、业务和开发人员之间的一致。我们考虑了哪些元素是可重用的,结构和网格,全局元素,一次性的,以及可访问性。我们还展示了浏览器之间可能的争用点和折衷点,以及如何解决它们。我们得到的 CSS 清晰、简洁、不复杂。覆盖选择器并不困难,并且很容易找到代码中的特定部分。最重要的是,它很快,具有最小的合理文件大小和很少的 HTTP 请求。

我们研究了设计库的构建,这对于实现这些设计的效率和一致性以及将来对网站的修改是至关重要的。我们尽可能地遵循自己的建议,并在我们不同意的地方给出理由。都是为了提高效率,减少冗余。

在四个附录中,您可以找到我们在虚构组织中实现的流程和规则的支持文档,这些文档旨在尽可能对您实用和有用。

我们真诚地希望你能喜欢这本书,并从生产力、成本、品牌认知、用户体验和性能等多个角度,很好地理解 CSS 是如何给你的网站带来巨大变化的。让我们一起努力来构建可扩展的、可访问的、智能的界面,这些界面看起来很美,并且工作起来很直观。每个团队都有自己的做事方式,只要每个人都同意相同的系统,那绝对没问题。我们很想听听你自己的经历。

感谢阅读。现在去写点代码吧!

十二、附录 1 :CSS 标准指南

本指南仅作为示例,并不详尽。目标是向你展示什么类型的内容可以成为所有 CSS 作者必须遵守的准则。指南的详细程度,或者如何划分不同的文档(例如,您可能希望将本指南和浏览器支持指南合并到一个文档中)由您决定。

这里被称为链接的页面实际上并不存在。它们表明您可能希望创建其他类型的文档,并在公司范围的中央存储库中提供这些文档。

每个指南都应该包括一个理由,以避免进一步的讨论。任何被否决的观点都应该和结论一起记录下来,这样就不会再进行不必要的讨论了。每一项都应编号,以便在文档内或跨文档引用。

Igloo 冰箱零件公司 CSS 标准指南

  1. 简介和惯例
  2. 履行
  3. 通则
  4. 评论
  5. 格式化
  6. 命名
  7. 排印
  8. 颜色

1。简介和惯例

这些 CSS 指南适用于所有由 Igloo 冰箱零件公司创建和管理的网站,并且在igloorefrigeratorparts.com域名下。

本指南中使用的关键字具有以下含义:

  • 必须遵守这条规则,没有例外。
  • 不得:这个规则意味着绝对禁止,没有例外。
  • 应该:除非有合理的理由,否则任何时候都应该遵守这条规则。
  • 不应:除非有合理的可接受的理由,否则应始终避免这种情况。
  • MAY :该规则允许在特定情况下实现特定措施。

有关可访问性指南,请阅读可访问性指南

您可以从“模板和资产”部分访问必要的模板和文件来启动一个新项目。

请参考浏览器支持指南了解关于浏览器支持、QA 的信息,以及如何避免一些常见渲染错误的提示。

2。履行

2.1 所有的 CSS 应该从外部加载,因为它支持缓存控制并使 HTML 更小。

2.2 外部 CSS 必须通过link元素引用,其中必须放置在文档的头部分,尽可能靠近顶部,但在title元素之后。这将提高性能,不会妨碍搜索引擎优化。

2.3 外部样式表必须使用@import导入 而不是,因为它会损害缓存并阻止渲染。调试文件是此规则的唯一例外。

2.4 在仅特定页面需要样式规则的情况下,不应使用文档头 CSS。这样做会使这些规则不可缓存且不一致。

2.5 所有链接的样式表必须存在(你不得**使用占位符)。丢失的文件会产生不必要的请求,这可能会影响性能,而 404 错误可能会影响 SEO。

2.6 CSS 文件应该在网站的“CSS”目录下。属于 CSS 的图像应该保存在“CSS”目录中名为“I”的目录中。这使得“css”目录与位置无关。

2.7 所有 CSS 文件必须通过调试文件中的@import语句引用,尽管可能有多个(一个全局文件和一个部分文件)。这有助于调试。

2.8 在生产中,提供的 CSS 文件必须通过 YUI 压缩器连接和缩小。调试文件必须用作 CSS 文件列表的来源。这提高了性能和可维护性。

2.9 你应该在适当的时候使用 CSS 精灵。这提高了性能,因为它减少了 HTTP 请求。这在针对低带宽设备时尤为重要。

2.10 你一定不要使用 CSS 表达式,因为浏览器支持很差,而且它们没有性能。

2.11 你不能使用 CSS 行为(HTC 文件),因为浏览器支持很差,它们会产生 JavaScript 依赖。

2.12 您不应该使用数据 URIs,因为它们比它们的等效文件要大,会破坏缓存控制。

3。通则

3.1 您应该使用 UTF-8 字符编码。这有助于避免本地化错误。

3.2 你不得使用内联 CSS。这样做会使这些规则不可缓存且不一致。

3.3 你不得使用!important。它可能会妨碍用户样式表的良好使用,并使其他作者很难覆盖该 CSS 声明。

3.4 您不应该使用通用选择器(*)创建规则,因为它可能会产生意想不到的效果,破坏继承性并影响性能。

3.5 您不得创建通用选择器(*)与其他选择器一起使用的规则,因为这将破坏继承并影响性能。

3.6 你应该避免使用高级的、复杂的选择器,或者 CSS3 选择器,当一个简单的类或 id 就足够了。复杂的选择器会影响性能。

3.7 你应该从不太具体的规则(p)开始,并在需要时增加具体性(从右到左)。aside p”)。这有助于在必要时覆盖规则,并保持规则简单。

3.8 每个将要使用的 HTML 标签必须至少有一个简单的 CSS 元素选择器,并应用基本样式,或者必须有意识地决定使用浏览器默认设置(例如,在表单元素中)。这确保了浏览器之间的一致性。

3.8 您不应为 0(零)值指定单位。这些都没必要。

3.9 你必须只用 CSS 动画做视觉装饰,不做功能性。不能指望对他们的支持。

3.10 你应该测试你的样式表是否有多余或重复的选择器。

3.11 应尽可能使用的速记属性。请注意,它们将覆盖单个属性。

3.12 你应该 而不是修改已建立的文件(缩小并连接的文件)。这会造成混乱,您的更改将会丢失。

3.13 除字体之外的所有内容都应以像素为单位调整大小。

3.14 每个页面应该在引用该页面的body标签上有一个 ID,这样就可以有针对性地定位。例如:

**<body id="home">

3.14 每个页面必须在body标签上有一个类,该类引用页面语言的语言和国家组合,这样就可以有针对性地使用。例如:

<body class="en-us">

4。评论

4.1 文件(CSS 文件顶部的初始注释)和节注释遵循 CSSDOC ( [cssdoc.net/](http://cssdoc.net/))格式。CSSDOC 提供了一种标准化的注释方式。CSSDOC 注释块(DocBlock)具有以下结构:

/**  * Short description  *  * Long description  *  * @tags (optional)  */

4.2 一个 CSS 文件必须在顶部包含一个文件注释,包括以下信息:文档标题、简短描述、详细描述(可选)、相关项目、作者、版权和颜色信息(可选)。例如:

/**  * Christmas theme  *  * CSS theme for the Christmas version of the Igloo Refrigerator  * Parts Inc website  *  * This theme should only be used between the dates of November  * 7th and January 7th  *  * @project     IRP Christmas Site  * @author      Design Team at IRP Inc  * @copyright   2011 Igloo Refrigerator Parts Inc  *  * @colordef    #111111; main text  * @colordef    #999999; headings  * @colordef    #9f0000; unvisited links  * …  */

4.3 CSS的所有相关块应该以这种格式加上一个节注释作为前缀,以帮助 CSS 文档的可视扫描和可搜索性:

/**  * Typography  *  * @section typography  **/

请访问“模板和资产”部分,访问带有注释示例的基本 CSS 模板。

4.4 您不应在评论中提及版本号或日期,因为版本控制系统会维护这些信息,如果手动管理,这些信息可能会过时。

4.5 你应该维护文件中各部分的目录。例如:

/**  * Table of contents  *  * Structure  * Generic typography and lists  * Global elements  * Links  * Forms  * Tables  * Visual media  * Components  * Reusable  * One-offs  */

4.6 需要计算的值应该有一个行内注释,显示得出该值的必要步骤。例如:

#main {         margin-left: 220px; /** aside width + 20px of margin */ } aside {         width: 200px;         float: left; }

4.7 需要在不同文件中更新的属性应被正确注释。比如修改min-height属性时,IE 6-only 样式表中的height属性需要同时更新。两个属性旁边的注释应该反映这一点。

5。格式化

5.1 规则的左括号应该与最后一个选择器在同一行,选择器和括号之间有一个空格。每个属性/值对应该在一个单独的行中声明,并带有一个缩进标签。规则的右括号应该在单独的一行。每个属性应该在冒号和属性值之间有一个空格,并以分号结束。这里有一个例子:

#content {         color: red;         font-size: 1em; }

5.2 多个选择器规则应该每行声明一个选择器。例外可能适用于重置样式表或其他有许多短选择器的多重选择器规则。

5.3 供应商特定属性包含在所有其他属性之后,以形成清晰的视觉区别。指定的 W3C 名称必须在特定于供应商的替代项之后声明(例如,border-radius 必须在-moz-border-radius-webkit-border-radius之后声明)。

6。命名

6.1 类名和 ID 名必须小写。类别和 ID 名称应该只包含一个单词。如有必要,单独的单词必须通过驼峰式大小写分隔,例如:highlightModule。在使用骆驼大小写和首字母缩写词的情况下,如果首字母缩写词在开头,它应该是小写的(例如,wwwLink);否则大写(linkWWW)。

6.2 类别和 ID 名称必须仅使用字母数字字符和连字符。它们不得包括下划线、正斜杠或反斜杠或星号;或者以数字字符开头。

6.3 类名和 ID 名应该尽可能具有语义,而不是表示性的。例如,你应该将警告消息样式命名为“警告”,而不是“红色”你应该总是尝试使用易于识别的类名和 ID 名,比如“nav”、“side”或“footer”

6.4 您应该使用微格式或等同物,如微数据或 RDFa,如果存在适当的用途。

6.5 名称空间必须用连字符与类名或 ID 名的其余部分隔开。例如:“igr-blogWidget”,其中“igr”是命名空间。

7 .。排印

7.1 您必须仔细选择字体堆栈(遵守品牌指南)。 1 从理想字体开始;随后是更可能安装在用户系统上的字体;随后是安装在大多数用户系统中的字体;最后这些默认fonts: sans-serifserif,monospace中的一个。

7.2 当你用大写字母显示文本时,你必须通过text-transform属性转换文本。这有助于以后设计的改变。

7.3 字体应使用 ems 调整的大小,但可以用%或关键字值调整的大小。它们不能以像素为单位,因为这破坏了只缩放文本的能力。

8。颜色

8.1 使用十六进制颜色时,你必须用小写字母,因为这有助于提高压缩效率。

8.2 您可以使用以下小写的命名颜色:黑色、白色、红色、蓝色。命名的颜色更容易阅读。

8.3 您不应使用 8.2 中定义的颜色之外的命名颜色,因为它们的值可能会被开发人员解释。

8.4 具有图像背景的元素必须具有定义的后退背景颜色。这使得页面在加载图像时更容易使用,并避免了不协调的颜色变化。


我们不会给你提供品牌指南的例子,因为它们超出了本书的范围。我们假设您的组织已经有了它们。

8.5 当使用 RGBA 颜色时,你必须在它之前声明一个后退的十六进制颜色,这样不支持 RGBA 的浏览器仍然可以显示一种颜色。你不应该在非透明色就足够的地方使用 RGBA 色。

8.6 您不得使用与背景颜色相同的前景文本颜色来隐藏文本。这将导致搜索引擎惩罚搜索结果。

CSS 编码标准参考文献

存在许多示例编码标准。以下是我们从中获得的一些灵感,你可能也会觉得有用:

  • BBC 未来媒体标准和指南——层叠样式表(CSS)标准 1.3 版[www.bbc.co.uk/guidelines/futuremedia/technical/css.shtml](http://www.bbc.co.uk/guidelines/futuremedia/technical/css.shtml)
  • BBC 未来媒体标准和指南——颜色和颜色对比标准 1.7 版[www.bbc.co.uk/guidelines/futuremedia/accessibility/colour_contrast.shtml](http://www.bbc.co.uk/guidelines/futuremedia/accessibility/colour_contrast.shtml)
  • 英国广播公司未来媒体标准和指南――文本链接标准 v2.1 [www.bbc.co.uk/guidelines/futuremedia/accessibility/links.shtml](http://www.bbc.co.uk/guidelines/futuremedia/accessibility/links.shtml)
  • BBC 未来媒体标准和指南――浏览器支持标准 3.72 版[www.bbc.co.uk/guidelines/futuremedia/technical/browser_support.shtml](http://www.bbc.co.uk/guidelines/futuremedia/technical/browser_support.shtml)
  • Drupal CSS 编码标准[drupal.org/node/302199](http://drupal.org/node/302199)
  • WordPress CSS 编码标准[codex.wordpress.org/CSS_Coding_Standards](http://codex.wordpress.org/CSS_Coding_Standards)
  • 斯托扬·斯特凡诺夫的 CSS 编码惯例[www.phpied.com/css-coding-conventions/](http://www.phpied.com/css-coding-conventions/)
  • 安索帕北美代码标准[na.isobar.com/standards/](http://na.isobar.com/standards/)****

十三、附录 2:无障碍指南

您的组织用来确保网站可访问性的准则可能独立于您的编码标准。它们将比这里展示的例子更加全面,因为它们还应该涵盖标记、JavaScript 和内容。我们重复了编码标准文档中的一些项目,以便这两个文档作为独立的实体工作。

Igloo 冰箱零件公司 CSS 可访问性指南

  1. 简介和惯例
  2. 通则
  3. 排印
  4. 链接
  5. 颜色

1。简介和惯例

这些可访问性指南适用于所有由 Igloo 冰箱零件公司创建和管理的网站,并且在igloorefrigeratorparts.com域名下。

本指南中使用的关键字具有以下含义:

  • 必须遵守这条规则,没有例外。
  • 不得:这个规则意味着绝对禁止,没有例外。
  • 除非有合理的理由,否则应始终遵守这条规则。
  • 不应该:除非有合理的理由,否则应始终避免这一规则。
  • MAY :该规则允许在特定情况下实现特定措施。

关于编码标准,请阅读 CSS 标准指南

您可以从“模板和资产”部分访问必要的模板和文件来启动一个新项目。

请参考浏览器支持指南了解关于浏览器支持、QA 的信息,以及如何避免一些常见渲染错误的提示。

2。通则

2.1 站点必须满足优先级 1 的 WCAG 检查站([www.w3.org/TR/WCAG10/full-checklist.html](http://www.w3.org/TR/WCAG10/full-checklist.html))。

2.2 站点满足优先级 2 的 WCAG 检查站。

2.3 站点满足优先级 3 的 WCAG 检查站。

2.4 你不得使用!important。它可能会妨碍用户样式表的良好使用,并使其他作者很难覆盖该 CSS 声明。

2.5 你必须为网站上的每个页面在body元素上放置一个一致的类,给用户样式表一个钩子。例如:

<body class="iglooRefrigeratorSite">

2.6 您应该避免任何闪烁的内容。

2.7 你应该用大白鲨([www.freedomscientific.com/products/fs/jaws-product-page.asp](http://www.freedomscientific.com/products/fs/jaws-product-page.asp))、NVDA ( [www.nvda-project.org/](http://www.nvda-project.org/))或麦克·OS X 画外音([www.apple.com/accessibility/voiceover/](http://www.apple.com/accessibility/voiceover/))等屏幕阅读器查看网站。

2.8 你可以使用替代样式表,但是如果你这样做,你必须提供一种页面内切换的方法,而不是依赖于浏览器。

2.9 您不得依赖悬停状态或动画来传达信息。

3。排印

3.1 默认字体大小不得小于【12px。

3.2 网站必须接受至少两步(每个方向)的字体大小增减测试。

3.3 字体应使用 ems 调整的大小,但可以用%或关键字值调整的大小。它们不能以像素为单位,因为这会破坏某些浏览器中的纯文本缩放。

3.4 当你用大写字母显示文本时,你必须通过text-transform属性转换文本。这避免了屏幕阅读器拼写单词,好像它们是首字母缩写词。

4。链接

4.1 链接应该总是按照以下顺序为它们的已访问、悬停、活动和聚焦状态定义一个样式::link, :visited, :hover, :active, :focus

4.2 你不能**给不是链接的文本加下划线,因为这会让用户感到困惑。

4.3 链接应该有大的可点击区域。

5。颜色

5.1 已访问链接的颜色应该不同于未访问链接的颜色。对于色盲用户来说,这种色差必须明显。

5.2 文本和背景的颜色对比必须至少有 125 的亮度差和至少 400 的色差。使用 Jonathan Snook 的色彩对比检查([snook.ca/technical/colour_contrast/colour.html](http://snook.ca/technical/colour_contrast/colour.html))进行确认。

5.3 您不得使用任何色盲患者无法辨别的颜色组合。

5.4 您不得仅用颜色传达信息。

5.5 导航必须能够通过颜色以外的方式识别。

5.6 具有图像背景的元素必须具有定义的后退背景颜色。这使得页面在加载图像时更容易使用,并避免了不协调的颜色变化。

5.7 你应该用色盲模拟器测试网站的颜色组合,比如 Color Oracle ( [colororacle.cartography.ch/index.html](http://colororacle.cartography.ch/index.html))。**

十四、附录 3:浏览器支持指南

浏览器支持的准则可能独立于您的编码标准。所有这些指导方针都应该相互链接,并存在于维基上。有些项目可能会重复其他文件,使指南可以作为一个单独的实体。

Igloo 冰箱部件公司浏览器支持指南

  1. 简介和惯例
  2. 通则
  3. 避免常见错误
  4. 黑客和过滤器
  5. 设备

1。简介和惯例

这些浏览器支持指南适用于由 Igloo 冰箱零件公司创建和管理的所有网站,并且在igloorefrigeratorparts.com域名下。

本指南中使用的关键字具有以下含义:

  • 必须遵守这条规则,没有例外。
  • 不得:这个规则意味着绝对禁止,没有例外。
  • 除非有合理的理由,否则应始终遵守这条规则。
  • 不应:除非有合理的可接受的理由,否则应始终避免这种情况。
  • MAY :该规则允许在特定情况下实现特定措施。

关于编码标准和可访问性考虑,请分别阅读 CSS 标准指南可访问性指南

您可以从“模板和资产”部分访问必要的模板和文件来启动一个新项目。

2。通则

2.1 您必须根据分级浏览器支持页面上的分级浏览器支持图表测试网站。 1

2.2 您必须使用以下设置组合测试网站:CSS on/images on、CSS on/images off 和 CSS off/images off,因为它们涵盖了大多数真实场景。

2.3 每个设备上的位置必须清晰可辨且功能正常。

3。避免常见错误

3.1 你不应该在同一个元素上同时设置宽度/高度和填充,因为如果浏览器以怪癖模式呈现页面,这可能会导致布局中断。

3.2 绝对定位的元素应该定义水平和垂直定位,以避免继承问题。

3.3 浮动元素应该有一个固定的宽度,因为有些浏览器会有不可预测的行为。

3.4 你应该在 IE 6 中为height设置一个值,其中min-height是为其他浏览器设置的,因为这个浏览器不理解min-height

3.5 相对定位一个元素时,你应该确保你触发了 hasLayoutposition:relative不触发 hasLayout(见 4.7)。

4。黑客和过滤器

4.1 你不得使用可能潜在地针对未来浏览器的条件注释。例如,你不得瞄准 IE,但你可以瞄准 IE 6 或更低版本。

4.2 应避免的黑客攻击和过滤。当它们不可避免时,它们必须被适当地记录;你应该提供一个到文档化解决方案的链接,以及 bug 的通用名称,如下所示:

/** HACK_IE_LTE_6: “display: inline” fixes the Double Float Margin bug on IE5/6\. More on this bug/solution: http://www.positioniseverything.net/explorer/doubled-margin.html */

4.3 在 Internet Explorer 中使用黑客和过滤器时,它们必须以注释为前缀,如下所示:

HACK_IE_LTE_7:


我们在此不包括本文档,因为我们通常建议使用 Yahoo!分级浏览器支持作为一个开端。您应该根据您的报告工具和用户使用的浏览器来修改它。

此评论意味着对 IE 7:

HACK_IE_7:

这个评论意味着对 IE 版的一次黑客攻击。这种方法使得将来搜索这些黑客变得容易。

4.4 您不得使用黑客攻击此列表以外的浏览器:

  • 下划线黑客
    • 要针对 IE 6 及更低版本,请在属性名前加下划线。
    • 示例:_property: value;
  • 明星黑客
    • 要针对 IE 7 和更低版本,请在属性名前面加一个星号。
    • 示例:*property: value;
  • 反斜杠 9 hack
    • 要将 IE 8 及更低版本作为目标,请在该值后面加上反斜杠 9。
    • 示例:property: value\9;

4.5 为了瞄准 IE 的多个版本,你可以结合 4.4 中描述的技巧。例如:

 property: value\9; /* HACK_IE_8: Comment… */ *property: value;   /* HACK_IE_7: Comment… */ _property: value;   /* HACK_IE_LTE_6: Comment… */

4.6 如果 4.4 中没有提到您需要使用的黑客,您必须同意并将其添加到本文档中,然后才能使用。

4.7 你不应该zoom:1用于正常目的,而只是为了修复 IE 中无法用更干净的解决方案修复的 hasLayout。

4.8 你应该使用 CSS 验证作为修复可能错误的第一步。

5。设备

5.1 您必须根据移动分级浏览器支持页面中的移动分级浏览器支持图表测试网站。 2

5.2 背景图像应该应用于具有该设备特有的类的元素,而不是没有应用类的元素,因为这些图像可能会被不必要地下载。


2 我们在这里不包括这个文档,因为我们通常建议使用 jQuery Mobile 的移动分级浏览器支持([jquerymobile.com/gbs/](http://jquerymobile.com/gbs/))作为起点。您应该根据您的报告工具和用户使用的浏览器来修改它。

5.3 你应该支持打印样式表。主样式表应该将指向“所有”,这样就不会遗漏任何设备。

5.4 在打印样式表中,使用背景图像作为项目符号的列表应该恢复默认的项目符号,因为大多数浏览器在默认情况下不会打印背景图像。

5.5 对于印刷媒体来说,导航元素通常不是必需的,因此应该通过 CSS 隐藏。

5.6 在可能的情况下,浮动容器应该为打印样式表而不浮动。

5.7 在打印样式表中,字体大小应该用 pt 设置

5.8应该是外部链接上链接的href属性的指示。关于如何做到这一点,请访问一个列表:“CSS 设计:打印”([www.alistapart.com/articles/goingtoprint/](http://www.alistapart.com/articles/goingtoprint/))。

5.9 您应该确保表格和图像不会在分页符上被裁剪。

十五、附录 4:开发流程

你的开发过程指南应该和你的编码标准分开放在 wiki 上。这些过程可以(也应该)经常改变,所以需要有一个单一的授权点供团队参考。

Igloo 冰箱零件公司的发展过程

本文件描述了我们打算在组织内使用的流程。这个过程可以从一个团队修改到另一个团队,但是这个指南应该作为新项目的基线。这份文件应该放在维基上,应该鼓励围绕改进它的讨论。每个项目都应该根据自己的目的修改文档,使其更加符合预期的过程。

开发过程跨越了从接收需求到部署到生产的时间。

团队

该项目的团队定义如下:

  • 项目管理人
  • 服务器端开发人员 x 2
  • 前端开发人员 x 2
  • 测试员

迭代

该项目被分成两周的冲刺(或迭代)。这个长度足够长,以避免每次冲刺的成本和时间影响,但又足够短,以对变化做出快速反应。每次迭代将在星期一开始,在之后的第二个星期五结束。在项目开始之前,我们必须已经有了业务需求和设计的列表。这些不应该改变,但它们可能会改变,这个过程就是为了适应这一点。

第一次冲刺被称为零迭代。在迭代 0 期间,任何阻碍团队执行其角色的事情都将被处理。这可能包括设置环境和构建、向工具添加用户或许可证、购买软件和硬件等等。在此迭代结束时,团队将拥有以下内容:

  • 对需求的良好理解
  • 代表整个项目的故事的优先列表
  • 系统预期架构的概念
  • 团队成员开始开发所需的一切
  • 具有必要的构建脚本和调试方法的工作持续集成环境

故事

在迭代 0 期间,需求将被分解成当前时态的故事,如下所示:

  • 登录 web 服务存在
  • 用户可以登录

每个故事都有一个验收标准,这是用来定义一个完整的故事。测试人员应该熟悉边缘情况和安全漏洞,他们会帮助解决这些问题。在这个过程中,可能会发现其他需求。非功能需求 1 也应该被考虑,因为故事也可能由此产生。验收标准示例如下:

  • 登录 web 服务存在
    • 服务必须接受两个参数:用户名和密码
    • 服务必须忽略任何进一步的参数
    • 当用户名和密码与数据库中的条目匹配时,必须设置登录的 cookie 和用户重定向到安全页面
    • 如果用户名和密码与数据库中的条目不匹配,用户必须被重定向回原始页面并显示错误

这些故事将在 JIRA 和绿漏斗中被追踪。这些故事也将按优先顺序排列。没有哪两个故事具有相同的优先级。

结构和版本控制

我们将使用 SVN。我们的(前端)文件夹结构如下:

/     css/             build/                     [Build files]             i/                     [CSS imagery]             [CSS files]             all-debug.css     i/             [Imagery]     js/             [JS files]     [HTML files]     favicon.ico


非功能需求是那些描述系统应该如何而不是应该做什么的需求。例子包括正常运行时间、性能、可靠性和可用性。

所有的 CSS 文件都将建立在css文件夹中。需要多少就有多少,以使开发容易。每个 CSS 文件都将在@import 语句的all-debug.css中被引用。这里有一个例子:

`@charset "UTF-8";

@import url(reset.css);
@import url(main.css);`

构建脚本 2 (在构建目录中)将手动运行,该脚本将解析该文件,连接所有引用的文件(以@charset为前缀)并将这些文件保存为all-concat.css.最后,它将使用 YUI 压缩器缩小该文件并将其保存为 all.css。在开发中,HTML 文件将指向all-debug.css,但在生产中,它们将指向all.css. all-concat.css可能对调试构建过程有用。每当一个 css 文件被提交到 SVN 时,CSS 目录上的一个 SVN 钩子将自动触发这个构建脚本。

计划会议

在迭代 0 之后的每个 sprint 的开始,在上午 10 点,团队将召开一个计划会议,由项目经理主持。首先,项目经理将计算他可用的资源。我们将只需要服务器端和前端开发人员的总数。我们假设团队的每个成员每天有六个小时的工作时间来开会、回复邮件等等。我们会考虑计划的假期或休息日。我们将从这个迭代的第一天和最后一天的会议总数中减去三个小时。例如,这将为我们提供以下总数:

  • 服务器端开发人员
    • 2 *(6 * 10)-3)= 114 小时
  • 前端开发人员
    • 2 *(6 * 10)-3)= 114 小时

接下来,最高优先级的故事将被分解成任务。这些任务是为开发人员准备的,所以它们需要对开发人员有意义。每个任务都将被标记为服务器端或前端任务,并且每个任务都需要一个针对它的时间估计。估计值应该能被三个小时整除——如果少于三个小时,多个任务可能会合并在一起。估计应该是四舍五入。当估计任务的适当时间时,两个适当的开发人员应该进行对话,直到他们都同意估计时间,尽管团队的其他成员可能会参与讨论。任务应该(像故事一样)按优先顺序排列。以下是任务示例:

  • 故事:登录 web 服务存在
    • 前端:创建 HTML—3 小时
    • 服务器端:创建服务器端表单验证—3 小时
    • 前端:创建前端表单验证—6 小时
    • 前端:创建 CSS—6 小时
    • 服务器端:设置 cookie—3 小时
    • 服务器端:根据数据库检查详细信息—6 小时
    • 服务器端:实现重定向—3 小时 3

本书的网站上有一个构建脚本的例子。

项目经理应该根据他可用的总资源来计算。一旦对这个故事进行了评估,剩下的可用资源是 99 个前端开发小时和 99 个服务器端开发小时。这个故事及其任务现在应该在我们的工具(GreenHopper)中移动到迭代中。接下来,团队将对剩余的最高优先级的故事做同样的事情,并将继续这个过程,直到没有更多的时间可用。最后,将估计另一个故事,并将其视为项目的延伸,即我们可能无法实现这一点,但如果我们实现了,我们会实现的。

站立会议

在第一次计划会议后,团队可以开始开发。每天上午 10 点会有一个站立会议,由项目经理主持。任何迟到的团队成员都会被扣分。三次标记后,该成员必须在下一次会议上购买整个团队的甜甜圈!公司代表将被鼓励参加这些会议,但不应发表意见。会议期间,每个人都将聚集在一个显示 JIRA 项目当前状态的屏幕周围。这会让你立刻清楚每个人都在做什么。项目经理应该突出任何看起来需要很长时间才能完成的任务,任何没有正在进行的任务的开发人员,并隔离任何阻碍团队完成任何工作的东西(以及创建解决这些问题的行动)。

日常发展

在会议之外,开发人员应该从队列中取出优先级最高的任务,并将其标记为进行中。他们应该什么也不做,只是继续工作,直到任务完成。当它完成时,他们应该添加一个注释,让测试人员知道如何测试该任务,然后继续下一个可用的任务。

测试人员应该签署任何已完成的任务,并将其标记为完成。如果它因为某种原因没有通过测试,测试人员应该添加一个注释来解释它是如何没有通过测试的,并将它移回任务队列中。只有从事该任务的开发人员应该去做。

当一个故事的所有任务都被标记为完成时,测试人员和项目经理应该一起检查这个故事的所有验收标准。如果一切都可以接受,这个故事将被标记为完成。

在迭代过程中,sprint 中不应该有任何改变或添加。然而,只要有可能,可以将新的故事添加到 backlog 中,或者重新排列当前 sprint 之外的故事的优先级。

总结会

每次冲刺的最后一天都有两次会议。首先,由项目经理主持的总结会议将在下午 3 点举行。在这个会议中,任何未完成的任务和故事都将被移回到 backlog 中,项目经理将计算出 sprint velocity。这是在之前的 sprint 中实际完成的估计工作的小时数。随着时间的推移,这个值可以用来计算实际达到的平均小时数,这将有助于在未来的迭代中进行更准确的估计。


请注意,CSS 将是最后发生的事情。这是为了在过程中尽可能晚地修改设计。设计是最有可能改变的东西,虽然我们应该尽量避免这一点,但我们也应该为此做好准备。

回顾

每个 sprint 最后一天的第二次会议是回顾会议,由项目经理主持。回顾的目的是讨论迭代进行得如何,出现了什么问题,以及如何解决这些问题。它也应该被用来认可任何在前一次迭代中表现良好的团队成员。如果团队实现了高速度,在可能的情况下,我们会带团队去附近的酒吧或餐厅,在那里开会作为奖励。

回顾的形式如下:

  • 首先,团队(不包括项目经理)将花一分钟在便利贴上写下他们认为在前一次迭代中进展顺利的事情。
  • 项目经理将仔细检查这些,把相似的项目放在一起。一堆物品越多,团队对这些特定物品的感觉就越积极。每一个都应该加以讨论,并向适当的成员表示祝贺。
  • 接下来,团队(不包括项目经理)将花一分钟在便利贴上写下他们认为在前一次迭代中做得不好的事情。
  • 项目经理将仔细检查它们,对于每一个项目,必须记录一个行动来解决它。每个行动必须有一个人负责执行(通常是项目经理),并且必须在下一次回顾时完成。
  • 如果流程需要改变,我们会改变!
  • 最后,团队应该就所有事情的进展进行非正式的交谈。这是提出一般抱怨或与工作无关的问题的好时机,也是团队团结的好时机。

整个项目的最后一次回顾将是项目回顾。这将采取与任何其他回顾相同的形式,但是将基于整个项目,而不仅仅是前一次迭代。项目经理可能希望向比他的团队更广泛的观众展示他的发现,认可他们,并分享他们可能已经实现的对他们的过程的任何改变,这可能对整个组织有帮助。

调度

下面是迭代计划的快速回顾:

  • 星期一
    • 上午 10 点计划会议
  • 星期二至星期五
    • 上午 10 点单口相声
  • 周一至周四
    • 上午 10 点单口相声
  • 星期五
    • 上午 10 点单口相声
    • 下午 3 点总结会
    • 下午 4 点回顾

保修

整个过程将被重复,直到没有更多的故事,或该项目被视为完成。在这一点上,项目被发布到一个质量保证(QA)的环境中,这涉及到更深入的测试,以确保项目为发布做好准备。然后,项目立即发布到生产,并被认为是在三个月的保修期内。

最后,团队将转移到下一个项目,尽管它必须能够解决在保修期内发现的任何进一步的错误。

posted @ 2024-08-13 14:07  绝不原创的飞龙  阅读(1)  评论(0编辑  收藏  举报