原文:CSS Mastery
一、奠定基础
电子补充材质
本章的在线版本(doi:10.1007/978-1-4302-5864-3 _ 1)包含补充材质,可供授权用户使用。
人类是天生好奇的物种。我们只是喜欢摆弄东西。当我们在办公室拿到新的 Parrot AR 无人机时,我们甚至在看说明书之前就已经把它拆成了碎片。我们喜欢自己解决问题,创造自己的思维模式来思考事物的行为。我们蒙混过关,只有在出现问题或出乎我们意料时才求助于手册。
学习级联样式表(CSS)的最好方法之一是直接开始动手修改。事实上,这可能是你们中有多少人学会了编码;通过从博客中获取技巧,查看源代码以了解您最喜欢的设计者是如何实现特定效果的,以及通过浏览开放源代码库来获取代码片段。几乎可以肯定,你一开始并没有阅读完整的规范,这足以让人昏昏欲睡。
修补是一个很好的开始方式,但是如果你不小心的话,你可能会误解一个重要的概念或者为以后的工作埋下隐患。我们知道;我们已经这样做过好几次了。在这一章中,我们将回顾一些基本但经常被误解的概念,并向你展示如何让你的 HTML 和 CSS 保持清晰和良好的结构。
在本章中,您将了解:
-
可维护性的重要性
-
HTML 和 CSS 的不同版本
-
未来友好和向后兼容代码的策略
-
给你的 HTML 增加意义并使用新的 HTML5 元素
-
向 HTML 添加适当的样式挂钩
-
用 ARIA、微格式和微数据扩展 HTML 语义
-
浏览器引擎模式和验证
构建您的代码
大多数人不会考虑建筑物的地基。然而,如果没有坚实的基础,大多数建筑都不会屹立不倒。虽然这本书是关于 CSS 技术和概念的,但是如果没有一个结构良好且有效的 HTML 文档,你将要学习的很多东西是不可能的(或者至少是非常困难的)。
在本节中,您将了解为什么结构良好且有意义的 HTML 对于基于标准的开发至关重要。您将了解如何为您的文档添加更多的含义和灵活性,这样做可以使您作为开发人员的工作更加轻松。但是首先是一个非常重要的话题,不管我们碰巧用什么语言工作。
可维护性
可维护性可以说是任何好的代码库最重要的特征。如果你的代码开始失去结构,变得难以阅读,那么很多事情都会变得困难。如果您正在与不可读和脆弱的代码作斗争,添加新功能、修复 bug 和提高性能都会变得更加复杂和令人沮丧。在某些情况下,情况变得如此糟糕,以至于开发人员会完全拒绝进行更改,因为几乎每次他们这样做时,都会有一些东西出错。这可能导致没有人喜欢在网站上工作的情况,或者在非常糟糕的情况下,导致严格的变更控制过程,发布只能每周一次,甚至每月一次!
如果你正在构建一个要交给客户或其他开发团队的网站,可维护性就更加重要了。关键是你要提供易于阅读、意图明确的代码,并且针对变化进行了优化。“唯一不变的是变化”在这里是一个特别合适的陈词滥调,因为谁的项目没有不断变化的需求,以及不断的特性请求和错误修复?
随着代码库的增长,CSS 是最难维护的语言之一,即使是相对较小的网站的样式表也可能很快失控。其他现代编程语言具有内置的变量、函数和名称空间等特性;默认情况下,所有有助于保持代码结构化和模块化的特性。CSS 没有这些特性,所以我们需要将它们构建到我们使用语言和构建代码的方式中。当我们在整本书中讨论不同的主题时,你会发现可维护性的主题几乎贯穿了所有的主题。
标记简史
网络的力量在于它的普遍性。无论是否残疾,每个人都能获得服务是一个重要方面。
—蒂姆·伯纳斯·李
蒂姆·伯纳斯·李在 1990 年创造了 HTML,目的是格式化科学研究文件。它是一种简单的标记语言,能够赋予文本基本的结构和含义,如标题、列表和定义。这些文档通常很少或没有视觉修饰,可以很容易地被计算机索引,并由人们使用纯文本终端、网络浏览器或屏幕阅读器(如果需要)来阅读。
然而,人类是非常视觉化的生物,随着万维网的流行,HTML 开始获得创建演示效果的特性。人们将使用字体和粗体标签的组合来创建特定的视觉效果,而不是使用标题元素作为页面标题。表格被当作一种布局工具,而不是显示数据的方式,人们会使用块引用元素来缩进文本,而不是表示引用。很快,HTML 失去了给内容提供结构和意义的主要目的,变成了字体和表格标签的混乱。网页设计师为这种标记起了一个名字;他们称之为标记汤(见图 1-1 )。
图 1-1。2000 年 8 月 14 日 abcnews.com 的头条新闻的标记使用表格作为布局,大而粗的文本作为标题。代码缺乏结构,难以理解
网络已经变得一团糟,CSS 被创造出来帮助整理东西。CSS 的主要目的是允许 HTML 中的表示规则被提取出来并放入它们自己的系统中;为了分离内容和呈现。这鼓励了意义和语义潜移默化地回到 HTML 文档中。像字体标签这样的表示性标签可以被抛弃,布局表格可以被慢慢取代。这对于网站的可访问性和速度来说是一个福音,但 CSS 也为网站设计者和开发者提供了许多好处:
-
一种专门设计用来控制视觉样式和布局的语言
-
可以更容易地在一个站点中重用的样式
-
通过关注点分离改进代码结构
关注点分离
关注点分离的概念在软件开发中很常见。在 Web 上,它不仅可以应用于标记和样式的分离,还可以应用于样式的编写方式。事实上,这是确保代码可维护性的主要方法之一。
Unix 开发社区中有一个常见的短语,通过“小块、松散连接”来表达这个概念“一小块”是专注于做好一件事的代码模块。因为它与其他组件是“松散连接”的,所以该模块可以很容易地在系统的其他部分重用。Unix 中的“小片段”可以是字数统计功能,它可以处理你输入的任何文本。在 web 开发中,“一小块”可以是产品列表组件,如果松散耦合,它将可以在站点的多个页面上重用,或者在布局的不同部分重用。
你可以把这些小段代码想象成乐高积木。每一块砖都非常简单,但是它们可以以多种方式连接在一起,创造出极其复杂的物体。在本书的最后,在第十二章中,我们将回到这个主题,并研究如何以结构化的方式使用这个策略。
HTML 和 CSS 的不同版本
CSS 有各种不同的版本,或者说“层次”,了解一下这些版本的含义以及它们如何影响你应该或不应该使用的 CSS 特性是很有好处的。万维网联盟(W3C)是负责 Web 技术标准化的组织,它的每个规范在最终成为 W3C 推荐标准之前都经历了许多发展阶段。CSS 1 在 1996 年底成为 W3C 的推荐标准,包含非常基本的属性,如字体、颜色和边距。CSS 2 在 1998 年成为一个推荐标准,并增加了高级概念,如浮动和定位,以及新的选择器,如子、相邻兄弟和通用选择器。
CSS 3 是一个稍微不同的野兽。事实上,这里的并不是css3 本身,而是每个独立升级的模块集合。当一个模块规格继续改进一个现有的概念时,它从第 3 级开始。如果它是一种全新的技术,它从第 1 级开始。当使用 CSS 3 这个术语时,它通常指任何足以成为模块规范一部分的新东西。模块的例子包括“CSS 背景和边框级别 3”、“选择器级别 4”和“CSS 网格布局级别 1”这种模块化方法允许不同的规范以不同的速度发展,一些 3 级规范,如“CSS 颜色 3 级”,已经作为推荐标准发布。其他人处于候选人推荐状态,许多人仍处于工作草案状态。
虽然 CSS 3 的工作大约在 CSS 2 发布的时候就开始了,但是这些新规范的进展最初是缓慢的。因此,在 2002 年,W3C 发布了 CSS 2 修订版 1。CSS 2.1 修复了 CSS 2 中的错误,删除了浏览器中不太支持或不存在的功能,并在总体上进行清理,以提供更准确的浏览器实现情况。CSS 2.1 在 2011 年 6 月达到推荐状态,这是在 CSS 3 的工作开始十多年之后。这让你知道标准机构和浏览器制造商需要多长时间才能确定这些技术的工作原理。也就是说,当特性处于草案阶段时,浏览器通常会提供对特性的实验性支持,而在候选推荐阶段,事情通常是相当稳定的。某样东西成为可用技术的日期通常比它成为推荐的日期要早得多。
HTML 的历史同样复杂。1999 年,HTML 4.01 成为推荐标准,W3C 开始关注 XHTML 1.0。XHTML 1.1 本应遵循这一标准,但它强加的严格程度被证明是不切实际的,并且在 web 开发社区的成员中不受欢迎。实质上,网络主要语言的发展停滞了。
2004 年,一些公司成立了网络超文本应用技术工作组(WHATWG ),并开始制定一套新的规范。W3C 在 2006 年认识到这项工作的必要性,并加入了这项工作。2009 年,W3C 完全放弃了 XHTML,正式接受了 WHATWG 的新标准,即 HTML5。最初,WHATWG 和 W3C 都协调了他们在标准方面的工作,但是随着时间的推移,他们的关系变得复杂了。今天,他们编辑了两个独立的标准,一个来自 WHATWG,被称为 HTML,另一个来自 W3C,被称为 HTML5。是的,我们知道,这有点疯狂。幸运的是,这两个标准非常接近,所以将 HTML5 作为一个单独的东西来说还是有意义的。
我应该使用什么版本?
设计师和开发人员经常问他们应该使用哪个版本的 HTML 或 CSS,但是这个问题没有简单的答案。尽管规范为标准和 web 技术开发工作提供了一个焦点,但它们在很大程度上与设计人员和开发人员的日常工作无关。重要的是知道 HTML 和 CSS 的哪些部分已经在浏览器中实现,以及这些实现有多健壮和无错误。它们是应该谨慎使用的实验特性吗?或者,它们是否健壮且经过良好测试,在大量浏览器上实现匹配?
理解浏览器支持的状态是当今编写 CSS 和 HTML 最棘手的部分之一。有时候事情似乎进展得很快,你必须努力工作才能跟上。在其他时候,它会感觉慢得令人沮丧。在这本书里,你会看到关于 HTML 和 CSS 各种特性的浏览器支持说明,以及关于如何和何时应该考虑使用它们的提示。不可避免地,这里打印的信息会过时,所以你自己跟上这些信息是很重要的。
有几个学习浏览器支持的好地方。对于 CSS 属性,“我可以使用”网站(caniuse.com
)允许你搜索一个属性或一套属性,包括桌面和移动浏览器中支持它的浏览器的百分比统计。另一个雄心勃勃的计划是 http://webplatform.org 的 ??,这是 W3C 和几个浏览器制造商和行业巨头之间的合作,试图收集和合并他们各自支持 CSS、HTML、JavaScript APIs 等的文档。然而,正如大型项目往往会做的那样,将这种规范的 web 技术文档放在一起需要花费大量的时间。在这种情况下,Mozilla 的开发者文档 MDN(【http://developer.mozilla.org】)被普遍认为是黄金标准。
当讨论浏览器支持时,重要的是要接受并非所有的浏览器都是生来平等的;他们永远也不会。如今只有极少数浏览器支持 CSS 3 的一些特性。例如,直到 11 版的 Internet Explorer 和 6.1 版的 Safari 才支持灵活框布局(或简称为 flexbox )。即使需要支持遗留浏览器,也不代表 flexbox 一点用都没有。您可能会避免在站点的核心布局中使用 flexbox,但是您仍然可以选择在特定的组件中使用它,因为它的强大功能非常有用,并且确保在不理解属性的浏览器中有一个可接受的后备。判断向后兼容性和未来友好代码的能力是定义真正 CSS 大师的一部分。
渐进增强
平衡向后兼容性与最新 HTML 和 CSS 特性的能力涉及到一种称为渐进增强的策略。这代表的基本意思是“从使它在最小公分母上工作良好开始,但是在它们被支持的地方自由地进一步发展。”使用渐进式增强意味着您将在“层”中编写代码,其中每个连续的增强只有在被支持或被认为合适时才被应用。这听起来可能很复杂,但好消息是 HTML 和 CSS 都部分内置了这一点。
对于 HTML,这意味着未知的元素或属性一般不会给浏览器带来麻烦;它会毫无怨言地将它们一扫而光,但可能不会将结果应用到页面的工作方式上。例如,您可以使用 HTML5 中定义的新型输入元素。假设您有一个电子邮件地址的表单域,标记如下:
<input type="text" id="field-email" name="field-email">
您可以像这样更改 type 属性的值:
<input type="email" id="field-email" name="field-email">
没有实现新字段类型的浏览器将简单地回答“我不知道那是什么意思”,并退回到默认的类型值,即“文本”,就像第一个例子一样。理解“电子邮件”类型的新浏览器会知道用户应该在这个字段中输入什么类型的数据。在许多移动设备上,软件键盘会调整以显示为输入电子邮件地址而优化的视图,如果你在较新的浏览器中使用内置的表单验证支持,这也会起作用。我们已经逐步增强了页面,对老版本浏览器的用户没有负面影响。
另一个简单的变化是将文档类型声明从 HTML5 标准更新为新的、更短的版本。文档类型,或简称为 doctype,是 HTML 文档顶部的一位,它被认为是关于文档中使用的标记语言版本的机器可读提示。在旧版本的 HTML 和 XHTML 中,这曾经是一件漫长而复杂的事情,但在 HTML5 中,它被简化为:
<!DOCTYPE html>
您可以安全地切换到使用此 doctype 编写 HTML 文档,因为 HTML5 语法和 doctype 向后兼容。在接下来的章节中,我们将进一步了解 HTML5 中的一些新元素,但如果你需要更多关于如何开始编写 HTML5 标记的深入信息,请查看 Jeremy Keith 在 http://html5forwebdesigners.com的为网页设计师编写的 HTML5。
当涉及到浏览器如何解释新属性时,CSS 中的渐进式增强以类似的方式工作。浏览器无法识别的任何属性或值都会导致它丢弃该声明,因此添加新属性不会有任何不良影响,只要您提供合理的后备。
例如,许多现代浏览器支持颜色值的 rgba 函数符号。它允许您使用红色、绿色和蓝色通道的单独值以及透明度值(称为 alpha 通道)来指定颜色。我们可以这样使用它:
.overlay {
background-color: #000;
background-color: rgba(0, 0, 0, 0.8);
}
该规则规定,具有覆盖类名的元素应该具有黑色背景色,但是随后立即使用 rgba 将背景色重新声明为略微透明的黑色。对于不理解 rgba 表示法的浏览器,第二条语句将被忽略,元素将具有纯黑色背景色。对于那些理解 rgba 符号的浏览器来说,第二条语句覆盖了第一条语句。因此,即使 rgba 表示法并不是在所有地方都受支持,我们仍然可以使用它,只要我们先使用后备声明。
供应商前缀
浏览器制造商使用同样的原理在他们的浏览器中引入实验性的特性。他们通过在属性名或属性值前添加一个特殊的字符串来实现这一点,这样只有他们自己的浏览器引擎会应用它,而其他浏览器会忽略它。这使得浏览器制造商可以在规范缺失或不成熟时引入新功能。如果不同的浏览器对新特性有不同的解释,样式表作者可以在不破坏页面的情况下尝试它们。例如:
.myThing {
-webkit-transform: translate(0, 10px);
-moz-transform: translate(0, 10px);
-ms-transform: translate(0, 10px);
transform: translate(0, 10px);
}
这将一个转换应用到带有几个不同前缀的元素(我们将在第十章中讨论)。那些以-webkit-开头的应用于基于 webkit 的浏览器,比如 Safari。Google Chrome 和 Opera 基于 Blink 引擎,而 Blink 引擎最初是基于 WebKit 的,所以-WebKit-前缀通常也适用于它们。前缀-moz 适用于基于 Mozilla 的浏览器,如 Firefox,前缀-ms 适用于微软的 Internet Explorer。
最后,我们添加了无前缀版本,这样支持该属性标准化版本的浏览器就不会错过。从历史上看,开发人员在添加标准化版本时一直很马虎。这已经走得很远,一些浏览器制造商已经开始支持竞争引擎的前缀,只是为了确保流行的网站能在他们的浏览器上工作。这种混乱的结果是,大多数浏览器制造商不再使用厂商前缀。实验性的特性被隐藏在偏好标志后面,或者在特殊的预览版本中。
书中的例子大多只使用不带前缀的标准化属性,所以建议你去像caniuse.com
这样的网站查看,以确保当前的支持情况如何。
条件规则和检测脚本
对于更高级的情况,我们需要基于 CSS 支持的完全不同的解决方案,有@supports-block。这个特殊的块称为条件规则,它检查括号内的声明,并且仅在声明受支持时才应用该块内的规则:
@supports (display: grid) {
/* rules for when grid layout is supported go here */
}
这样做的问题是,这条规则本身相当新,所以我们只能将它用于任何传统浏览器都没有实现的前沿功能(例如,我们将在第七章中查看网格布局)。对于其他情况,我们可以使用 JavaScript 来判断某样东西是否受支持。这种类型的特性测试在几个 JavaScript 库中都有,最流行的是 Modernizr(【http://modernizr.com】??)。它的工作原理是将支持提示附加到 HTML 中,然后您可以将它作为 CSS 的基础。
我们将在接下来的章节中更仔细地研究类似的策略和工具,但是重要的是渐进式增强可以帮助我们摆脱对版本号和规范的过多担心。通过谨慎的应用,我们可以在适当的地方使用新的闪亮玩具,而不会把用户留在旧的浏览器上。
创建结构和语义丰富的 HTML
语义标记是任何好的 HTML 文档的基础。语义学是对意义的科学研究。在具有一组正式符号(如 HTML 及其元素和属性)的虚构语言的上下文中,语义指的是我们通过使用某个符号所表达的意思。简单地说,语义标记就是在正确的地方使用正确的元素,从而产生有意义的文档。
有意义的文档有助于确保尽可能多的人可以访问内容,无论他们是使用最新版本的谷歌浏览器,运行 Lynx 等纯文本浏览器,还是依赖屏幕阅读器或盲文显示器等辅助技术。无论项目后期可能需要什么样的图形或交互,文档的基本语义都不应该也不需要被破坏。
良好的结构化标记也意味着你的内容更容易被机器使用,特别是搜索引擎蜘蛛,如 Googlebot,它对页面进行索引和排序,以纳入谷歌的搜索结果。Googlebot 从您的页面中获取的数据越丰富,它就越有可能对这些页面进行正确的索引和排名。因此,你很可能会受益于搜索排名中更高的位置。
更重要的是,在 CSS 的上下文中,有意义的标记为您提供了一种简单的方法来定位您想要样式化的元素。它为文档增加了结构,并为您创建了一个基础框架。
事实上,许多制作 CSS 的现代方法都建议从网站的一组“基础”样式开始。图 1-2 所示的保罗·劳埃德的样式指南页面包含了他在个人博客上可能需要的所有看似合理的元素。它描述了如何以及何时使用它们,并且他的样式表确保了无论他在页面上添加什么元素,都将被适当地样式化,而不必做进一步的工作。
图 1-2。paulrobertlloyd.com 样式指南,位于paulrobertlloyd.com/about/styleguide/
保罗的样式指南包含了所有明显有意义的元素,例如:
-
h1、h2 等等
-
p、ul、ol 和 dl
-
强壮和 em
-
批量引用和引证
-
预编码和编码
-
时间、图片标题和标题
它还包括表单和表格及其相关元素的基本样式,包括:
-
字段集、图例和标签
-
标题、thead、tbody 和 tfoot
拥有这套基本样式的价值怎么强调都不为过。很明显,在设计和开发过程中,您需要很快开始继承和覆盖它们,但是拥有一个坚实的元素样式基础可以为您将来的工作做好准备。它也可以作为校样。当您对 CSS 进行更改时,您可以浏览样式指南中的组件,并验证您在处理其他样式时没有无意中覆盖某些样式。
类别和 ID 属性
有意义的元素提供了一个很好的基础,但是它们不会为你提供应用每一个视觉效果所需的所有“钩子”。十有八九你会希望根据上下文来调整基本元素的样式。我们需要一种在文档中提供其他样式“挂钩”的方法,一种常见的方法是使用 ID 和 class 属性。
添加一个 ID 或类属性并不会固有地给你的文档添加意义或结构。添加这些属性是一种通用的方式,允许其他东西与您的文档交互并解析您的文档,CSS 就是一种可以利用它们的东西。这些属性的价值在于它们可以包含您定义的名称。
这听起来很琐碎,但是命名是编写代码中最重要(通常也是最困难)的部分之一。选择一个名字可以让你陈述某个东西是什么,并暗示它的用途或者应该如何使用它。当你写代码时,清晰和明确是绝对重要的。所以让我们取一个简单的链接列表,并给它一个 class 属性,这个属性有一个很好的可读性和有用的值:
<ul class="product-list">
<li><a href="/product/1">Product 1</a></li>
<li><a href="/product/2">Product 2</a></li>
<li><a href="/product/3">Product 3</a></li>
</ul>
这里,我们使用 class 属性在文档中创建了一个产品列表模块。在 CSS 中,我们认为类名是定义事物的一种方式。product-list 类名为我们提供了一种方法来指定我们希望成为这种类型的任何列表。一旦我们创建了 CSS 来样式化我们的产品列表,我们不仅可以在这里使用它,还可以在网站的任何其他上下文中使用它——比如蓝图或模板。
即使我们添加了一个类名作为样式的显式挂钩,我们通常也应该避免使用一个在视觉上表明它看起来像什么的名字(我们将在第十二章讨论是否以及何时打破这个规则)。相反,我们应该选择一个表明组件类型的名称。例如,这里我们选择了 product-list,而不是一个通用名称,比如 large-centered-list。
您会注意到,在前面的例子中,我们选择了使用 class 属性,而不是 ID 属性。在用于样式化时,ID 和 class 属性之间有一些重要的区别,但是此时最适用的是一个 ID 名称只能应用于页面上的一个元素。这意味着它不能像为我们的产品列表这样的模块定义一个可重用的“模板”那样简单。如果我们使用了 ID 属性,我们就不能在每页上重用产品列表一次以上。
我们更喜欢使用 ID 属性来标识特定模块的单个实例。例如,我们的产品列表模块的一个实例可能如下所示:
<ul id="primary-product-list" class="product-list">
<li><a href="/product/1">Product 1</a></li>
<li><a href="/product/2">Product 2</a></li>
<li><a href="/product/3">Product 3</a></li>
</ul>
这是我们的 product-list 的另一个实例,它根据 class 属性选择样式,但是这里它也被定义为 primary product-list。每页只能有一个主要产品列表,这似乎是合理的,因此 ID 属性可能是一个合适的选择。然后,该 ID 可以用于向模块添加额外的覆盖样式,或者可以用于向模块添加一些与 JavaScript 的交互,或者作为导航的页面内锚。
实际上,使用 ID 属性作为 CSS 的挂钩通常并不特别有价值。如果您喜欢使用类进行样式化,并且只使用 id 来标识文档中的元素,而不是用于样式化,那么您通常会创建更简单、更易于维护的代码。我们将在第十二章更详细地讨论这个话题。
结构元素
HTML5 引入了一系列全新的结构元素:
section
header
footer
nav
article
aside
main
引入这些元素是为了创建 HTML 文档的逻辑部分。您可以使用它们来表示包含独立内容(文章)、导航组件(nav)、特定部分的标题(标题)等的部分。main 元素是最新添加的元素,突出显示了包含页面主要内容的区域。http://html5doctor.com 是一个很好的资源,可以让你深入了解所有这些新元素的正确用法。
除了 main 元素之外,所有这些新元素都可以在一个文档中多次使用,这给了机器和人类更好的解释机会。在这些新元素出现之前,您经常会看到具有相似类名的 div 元素,例如在标记博客帖子时:
<div class="article">
<div class="header">
<h1>How I became a CSS Master</h1>
</div>
<p>Ten-thousand hours.</p>
</div>
那些 div 元素没有为文档提供任何真正的语义值;它们可能只是作为样式挂钩,使用类名。前面片段中唯一有实际意义的部分是 h1 和 p,但是使用我们新发现的 HTML5 元素,我们可以改进一些东西:
<article>
<header>
<h1>How I became a CSS Master</h1>
</header>
<p>Ten-thousand hours.</p>
</article>
我们通过这一改变改进了 HTML 的语义,但是带来了意想不到的副作用。现在,我们唯一可以用来设计样式的挂钩是文章和标题元素。用于样式化它们的 CSS 选择器可能如下所示:
article {
/* styles here */
}
article header {
/* other styles here */
}
文章和标题元素都可以在页面的其他地方重用,用于显示博客文章之外的其他目的。如果它们现在被重用,并且我们将样式直接附加到选择器中的元素上,那么它们将会选择我们为博文设计的样式规则,不管它们是否适合新的情况。一种更灵活、更具前瞻性的方法是将这两个例子结合起来:
<article class="post">
<header class="post-header">
<h1>How I became a CSS Master</h1>
</header>
<p>Ten-thousand hours.</p>
</article>
关联的 CSS 规则然后可以使用类名来挂钩到这个结构:
.post {
/* styles here */
}
.post-header {
/* other styles here */
}
通过这个简单的改变,我们实际上展示了一个非常重要的概念。我们已经将文档的语义从样式化的方式中分离出来,使其更易于移植,目的更清晰,因此更易于维护。如果我们现在决定一篇文章不是包含该内容的最合适的元素,或者我们发现我们的内容管理系统(CMS)出于某种原因限制我们使用 div,我们不需要做任何进一步的更改。无论我们选择(或被迫)使用什么元素,我们与类属性挂钩的样式都会工作得很好。
旧互联网探索者和新元素
在大多数浏览器中,使用这些新元素可以很好地工作,但 Internet Explorer 8 和更早版本不会将样式应用于它不知道的元素。幸运的是,这可以通过使用一个叫做“填充”或“多填充”脚本的 JavaScript 片段来解决。
你可以在 https://github.com/aFarkas/html5shiv 找到这样一个脚本的版本。
它也包含在前面提到的 Modernizr 库中,我们将在接下来的章节中回到这个库中。
如果您预计很大一部分用户使用的是非常旧的浏览器,那么在严重依赖这些新元素时应该小心,因为这会导致额外的 JavaScript 依赖,从而使事情按预期工作。
使用 div 和跨度
所有这些新奇的语义元素并不意味着我们的老工具 div 元素是多余的。当没有其他更符合您的目的的语义正确的元素时,div 是用于对内容进行分组的合适元素。有时,纯粹出于样式目的,您需要向文档中添加额外的元素,例如围绕整个页面的包装,以帮助创建居中布局。
如果您可以使用一种更具语义的元素来构建您的内容,那么总是这样做,如果需要样式化,就给它一个合适的 class 属性。但是,如果您需要一个非语义元素作为额外的样式挂钩,请使用 div。有一个古老的术语叫做“divitis ”,指的是 HTML 作者倾向于在他们的标记中加入 div,或者对任何事情都使用 div,而不管是否有更合适的元素。只在必要的地方添加 div,以提供简单明了的造型挂钩,但不要因为不得不添加一些 div 而感到尴尬或羞愧。我们将在后面的章节中看到一些具体的例子,在这些例子中,一些额外的无意义的 div 对于创建干净的、可维护的代码变得非常有价值。
div 元素的一个关联是 span。像 div 一样,它没有语义意义,可以纯粹用于向文档添加表示挂钩。span 不同于 div,因为它是一个文本级元素,用于在一段文本流中提供结构。同样,在使用无意义的 span 之前,一定要确保没有语义丰富的 HTML 元素可以代替它。例如,time 应该用来标记时间和日期,q 用来标记引文,而通常的疑点 em 用来强调重音,strong 用来表示非常重要:
<p>At <time datetime="20:07">7 minutes past eight</time> Harry shouted, <q>Can we just end this, now!</q> He was <strong>very</strong> angry.</p>
重新定义的表示性文本元素
和元素是表示标记时代的残余,分别用来代表粗体和斜体文本。你可能会认为它们已经从新的 HTML5 规范中剔除了,但是它们实际上仍然存在。由于它们广泛出现在网络上的旧内容中,或者通过亚标准的 WYSIWYG 编辑器创建的内容中,HTML5 规范的编辑决定将它们留在那里,而不是更新它们的定义。
今天,元素代表了不同于其周围环境的内容,通常会被排版为斜体。HTML5 规范中的例子包括不同语言的表达式或船名。
元素有几乎完全相同的定义,但是对于传统上是粗体的内容。这里的例子包括产品名称或类别。
这听起来有点模糊,但重要的是,这两个元素与它们的表亲和**不同,因为它们没有说明其中内容的重点。大多数时候你会想要或**,因为它们是在一段文本中强调和强调的语义上正确的选择。******
扩展 HTML 的语义
长期以来,web 开发人员一直在探索向 HTML 有限的词汇中添加新的语义和结构的方法。内容中更丰富的意义表达为网络和围绕它构建的工具提供了各种可能性。尽管迈向语义网天堂的进程还没有快到令人眼花缭乱的地步,但在允许 HTML 作者向他们的文档中添加更细粒度和更具表达性的语义方面已经取得了一些积极的进展。
ARIA 角色属性
许多新的 HTML5 元素为可访问性优势开辟了可能性。例如,如果屏幕阅读器等辅助技术能够理解导航元素在页面中的位置和内容,它们就可以帮助用户跳过这个导航来找到内容,或者在需要时返回导航。
实现这一点的另一种方法是使用可访问的富互联网应用程序(ARIA ),它作为 HTML 的补充规范。ARIA 允许您为辅助技术提供更多的语义含义,通过指定文档包含的不同元素或它们提供的功能。例如,role="navigation “是所谓的” landmark role "属性,它声明一个元素具有导航角色。其他标志性角色包括:
-
旗帜
-
形式
-
主要的
-
搜索
-
补充的
-
内容信息
-
应用
ARIA 角色及其定义的完整列表可以在 ARIA 规范www.w3.org/TR/wai-aria/roles#role_definitions
中找到。
如果你需要一个何时使用里程碑式角色的简短分类,Paciello Group 的 Steve Faulkner 已经在blog . paci ello Group . com/2013/02/using-wai-aria-landmarks-2013/
上发布了如何以及何时使用它们的概述。
ARIA 还允许开发人员指定更复杂的内容或界面元素。例如,在 HTML 中重新创建音量控制滑块小部件时,它将包含一个值为 slider 的角色属性:
<div id="volume-label">Volume</div>
<div class="volume-rail">
<a href="#" class="volume-handle" **role="slider" aria-labelledby="volume-label" aria-valuemin="1" aria-valuemax="100" aria-valuenow="67"**></a>
</div>
额外的属性 aria-labelledby、aria-valuemin、aria-valuemax 和 aria-valuenow 都提供了额外的信息,辅助技术可以使用这些信息来帮助有视觉障碍、运动障碍或不同能力的用户使用滑块小部件。
添加关于 HTML 页面各种组件所扮演角色的额外语义信息也是为脚本和样式提供钩子的一个好方法,所以这是一个典型的双赢。
微格式
到目前为止,最广泛采用的扩展 HTML 语义的方式是微格式,这是一组标准化的命名约定和标记模式,用于表示特定类型的数据。这些命名约定基于现有的数据格式,如 vCard 和 iCalendar。例如,以下是一些以 hCard 格式标记的联系方式:
<section class="h-card">
<p><a class="u-url p-name" href="http://andybudd.com/">Andy Budd</a>
<span class="p-org">Clearleft Ltd</span>
<a class="u-email" href="mailto:info@andybudd.com">info@andybudd.com</a>
</p>
<p class="p-adr">
<span class="p-locality">Brighton</span>,
<span class="p-country-name">England</span>
</p>
</section>
用微格式标记的联系信息使开发人员更容易编写提取这些数据的工具。例如,浏览器插件可以在您浏览的页面中找到微格式,并允许您将联系人下载到您的地址簿中,或者将事件添加到您的日历应用程序中。一系列数据类型都有微格式:联系方式、事件、食谱、博客文章、简历等等。微格式也可以用来表达一段内容和该内容链接到的另一个 URL 之间的关系。
微格式之所以受欢迎,部分是因为它们易于实现,并且已经被包括 Yahoo!以及直接添加到 WordPress 和 Drupal 等出版工具中。2012 年一项关于结构化数据实现的研究(microformats.org/2012/06/25/microformats-org-at-7
)发现,微格式在网络上的应用最为广泛,但也有一些替代品在最近开始取得重大进展,比如微数据。
微观数据
微数据是 HTML5 中引入的,它提供了另一种向 HTML 添加结构化数据的方式。它的目的和目标与微格式非常相似,但是将微数据嵌入内容的细节有些不同。让我们看看如何使用微数据标记上一节中相同类型的联系信息:
<section itemscope itemtype="http://schema.org/Person">
<p><a itemprop="name" href="http://thatemil.com/">Emil Björklund</a></p>
<span itemprop="affiliation" itemscope itemtype="http://schema.org/Organization">
<span itemprop="name">inUse Experience AB</span>
</span>
<a itemprop="email" href="mailto:emil@thatemil.com">emil@thatemil.com</a>
</p> <p itemprop="address" itemscope itemtype="http://schema.org/PostalAddress">
<span class="addressLocality">Malmö</span>, <span class="addressCountry">Sweden</span> </p>
</section>
如本例所示,微数据语法比相应的微格式略显冗长;但这是有原因的。微数据被设计为可扩展的,因此它可以表示所需的任何类型的数据。它只是提供了一些表达数据结构的语法,但本身并没有定义任何特定的词汇表。这与微格式相反,微格式定义了特定类型的结构化数据,如 hCard 或 hCalendar。
微数据留给其他人来定义和记录特定的格式。我们在前面的例子中使用的格式是由 http://schema.org 定义的词汇表之一,它是由必应、谷歌和雅虎的代表创建的。这些搜索引擎使用它来帮助他们索引和排列页面,这意味着这些词汇表是帮助搜索蜘蛛丰富有效地索引您的内容的另一种方式。
确认
即使您的标记的核心是经过深思熟虑的,语义上也是合理的,但是仍然存在输入错误或格式错误会给您带来不可预见的麻烦的风险。这就是验证的用武之地。
现实世界中的大多数 HTML 文档实际上都不是有效的 HTML。用规范编写者的话说,他们是不符合规范的。这些文档的元素嵌套不正确,包含未编码的&符号,并且缺少必需的属性。浏览器非常优雅地处理这类错误,并且总是试图猜测作者的意图。事实上,HTML 规范中包含了处理无效 HTML 的规则,以确保浏览器开发者以一致的方式处理错误。
浏览器如此善于处理我们的错误,这一事实对整个网络来说是一件幸事,但这并不能免除我们在这方面的责任。我们应该尽可能尝试创建有效的文档。这样做将有助于我们更快地捕捉错误,或者完全阻止它们被引入。如果您有一个呈现或布局错误,但没有立即和明显的修复方法,一个好的第一步是验证 HTML,以确保您尝试的是一个格式正确的文档。
有许多工具可以帮助您验证 HTML。您可以使用 W3C 站点上的 HTML 验证器(validator.w3.org/
),或者与它通信的许多浏览器插件中的一个。例如,Web Developer extension(【http://chrispederick.com/work/web-developer/】??)可用于 Mozilla Firefox、Opera 和 Google Chrome,并具有验证公开可用网站和本地网站的选项(以及其他真正有用的功能!).或者,如果您的项目有任何类型的自动化构建或测试过程,您可以在这里包含 HTML 验证作为一个步骤。
CSS 验证也是可能的。W3C 在 http://jigsaw.w3.org/css-validator/有一个 CSS 验证器。有人可能会说验证 CSS 文件不如验证 HTML 重要——CSS 中的错误不太可能导致 JavaScript 失败或使使用辅助技术(如屏幕阅读器)的人无法访问您的页面。尽管如此,你应该确保不时地检查你的 CSS,以确保你没有犯任何简单的错误,比如忘记在度量中添加单位。
根据您在 CSS 验证器中选择的设置,您会收到许多关于在代码中使用供应商前缀的警告或错误。这些是非标准的属性或值,浏览器制作者在实现对 CSS 特性的实验性支持时,允许您将其设置为真实事物的替身。例如,display 属性的-webkit-flex 值是基于 webkit 的浏览器中 flex 属性的实验版本。这很可能被验证器标记为警告或错误,但是即使验证器对您大喊大叫,您的文件也能正常工作。你只需要确保你明白为什么它会把事情标记为有问题。
验证本身并不是目的,由于来自第三方的内容、笨拙的 CMS 系统或您可能想要使用的实验性 CSS 功能,许多本来很好的页面都无法通过验证。还有一个风险是,验证器实际上没有跟上标准和浏览器实现。所以不要在验证上较劲,而是把它作为一种手段,在错误引起太多连锁反应之前,捕捉那些容易修复的错误。
摘要
在这一章中,我们看了一些可以确保你在 HTML 和 CSS 方面有一个坚实基础的方法。您学习了一些关于 HTML 和 CSS 的历史,如何跟上变化,以及如何使您的代码既向后兼容又对未来友好。现在,您已经知道了编写可维护代码的重要性,以及一些构建 HTML 的方法,这样就可以轻松、一致地使用 CSS 样式。
在下一章中,我们将回顾一些基本的 CSS 选择器,然后从第 3 级和第 4 级选择器规范转移到一系列更高级的选择器。您将了解特殊性、继承性和级联,以及如何将它们用于创建高效的样式表。
二、让你的样式达到目标
一个有效的、结构良好的文档为你的样式的应用提供了基础。您可能已经在 HTML 中添加了适当的样式“挂钩”,或者随着页面设计需求的发展,您可能还会添加更多的样式。在这一章中,我们将看看我们可以用来定位 HTML 的选择器的范围,以及我们可以用来获得更多控制的额外钩子。我们将涵盖:
-
公共选择器
-
面向现在和未来的前沿选择器
-
特异性和级联的奇妙世界
-
将样式应用到页面
CSS 选择器
最基本的选择器是型和后代选择器。类型选择器用于定位特定类型的元素,比如一个段落(如下图所示)或者一个标题元素。您只需简单地指定想要样式化的元素的名称。类型选择器有时也被称为元素选择器。
p {
color: black;
}
Descendant 选择器允许您定位特定元素或元素组的后代。后代选择器由两个其他选择器之间的空格表示。在此示例中,只有作为块引号后代的段落元素才会缩进,而所有其他段落将保持不变:
blockquote p {
padding-left: 2em;
}
这两个选择器非常适合全面应用基本样式。为了更具体和有针对性地选择元素,可以使用 ID 和类选择器。顾名思义,这些选择器将以具有相应 ID 属性或类名值的元素为目标。ID 选择器使用散列字符来标识;类选择器用句点标识。本示例中的第一条规则将使介绍性段落中的文本加粗,第二条规则将使日期变灰:
#intro {
font-weight: bold;
}
.date-posted {
color: #ccc;
}
<p id="intro">Happy Birthday, Andy</p>
<p class="date-posted">20/1/2013</p>
有时,将 ID 和类选择器与类型和后代选择器结合起来会很有用,而不是将 ID 或类属性添加到每个要作为目标的元素中:
#latest h1 {
font-size: 1.8em;
}
#latest .date-posted {
font-weight: bold;
}
<article id="latest">
<h1>Happy Birthday, Andy</h1>
<p class="date-posted"><time datetime="2013-01-20">20/1/2013</time></p>
</article>
这些都是非常简单明显的例子。然而,您会惊讶地发现,仅仅使用到目前为止讨论的四个选择器,就可以成功地定位到多少个元素。通常,这些是可维护 CSS 系统的真正主力。其他高级选择器可能非常有用,但是它们没有这些更简单和更常用的选择器灵活和强大。
子代和同级选择器
在这些基本的选择器之上,CSS 包含了许多更高级的选择器。这些高级选择器中的第一个是子选择器。一个后代选择器将选择一个元素的所有后代,而一个子代选择器只针对元素的直接后代,或者子代。在下面的例子中,外部列表中的列表项将被赋予一个自定义图标,而嵌套列表中的列表项将保持不变(见图 2-1 )。
图 2-1。 child 选择器设计列表的子元素,而不是它的孙元素
#nav > li {
background: url(folder.png) no-repeat left top;
padding-left: 20px;
}
<ul id="nav">
<li><a href="/home/">Home</a></li>
<li><a href="/services/">Services</a>
<ul>
<li><a href="/services/design/">Design</a></li>
<li><a href="/services/development/">Development</a></li>
<li><a href="/services/consultancy/">Consultancy</a></li>
</ul>
</li>
<li><a href="/contact/">Contact Us</a></li>
</ul>
有时,您可能希望根据一个元素与另一个元素的接近程度来设置该元素的样式。相邻兄弟选择器允许你定位一个元素,这个元素的前面是另一个共享相同父元素的元素。使用相邻的 同级 选择器,你可以使顶层标题后的第一段加粗,灰色,并且比后续段落稍大(见图 2-2 ):
图 2-2。h2 后面的第一段有不同的样式
h2 + p {
font-size: 1.4em;
font-weight: bold;
color: #777;
}
这可能是一种有用的技术,但是请记住,用自己的类值(如 intro-text)来设计开头段落的样式可能会使 CSS 更简单、更灵活。然后,这个 intro-text 类可以用来设计不紧跟 h2 的其他段落的样式。
和+标记被称为组合符,因为它们描述了规则两边组合的方式。我们已经看到了子组合符 ( >)和相邻兄弟组合符 (+)的例子,但是我们还应该看看第三个组合符——一般兄弟组合符(∾)。回到上一个例子,您可以使用通用兄弟组合符来定位每一个具有前一个兄弟 h2 的段落元素。
h2 ∼ p {
font-size: 1.4em;
font-weight: bold;
color: #777;
}
注意
你可能已经注意到相邻兄弟和普通兄弟组合符不允许你选择前面的兄弟——例如,后面不是 h2 的段落。对这样一个有用的选择器有抵触的原因有点复杂,但与页面呈现性能有关。
一般来说,当元素出现在页面上时,浏览器会对它们进行样式化,而在应该对段落进行样式化时,HTML 源代码中的 h2 可能还不存在。先前的兄弟组合符意味着浏览器必须跟踪这些选择器,然后在处理文档时执行应用样式的附加步骤。
然而,有一个以前的兄弟选择器的建议版本正在考虑标准化,但到目前为止,这个想法是将它的有效性限制在 CSS 选择器的特殊用途上,比如当它们在 JavaScript 中被评估时,所以即使该标准在浏览器中发布,它也可能不会按照您希望的方式工作。
通用选择器
通用选择器就像一个通配符,匹配任何元素。像其他语言中的通配符一样,通用选择器用星号表示。单独使用时,通用选择器匹配页面中的每个元素。使用以下规则来删除每个元素的默认浏览器填充和边距可能很有吸引力:
* {
padding: 0;
margin: 0;
}
这可能会有许多不可预见的情况,特别是在表单 UI 元素(如按钮和选择元素)的格式化方面。最好更明确地说明您要重置的内容,如下例所示:
h1, h2, h3, h4, h5, h5, h6,
ul, ol, li, dl, p {
padding: 0;
margin: 0;
}
幸运的是,有许多小型的开源库可以帮你解决这个问题。埃里克·迈耶的 CSS 重置(meyerweb.com/eric/tools/css/reset/
)和尼古拉斯·加拉格尔的 normalize . CSS(necolas.github.com/normalize.css/
)就是很好的例子。后者采用了一种略有不同的方法:Normalize.css 确保所有元素在不同浏览器中以一致的样式开始,而不是将边距和填充重置为 0。我们认为这是一组比简单地将所有内容重置为 0 稍微安全的默认设置。
当然,您不必只使用通用选择器来设置文档中每个元素的属性。您还可以将它与组合子一起使用,以特定的嵌套级别为目标,其中嵌套级别很重要,但元素的类型不重要。举个例子:
.product-section > * {
/* ... */
}
这将针对作为具有 product-section 类名的元素的直接后代的任何元素,但是不关心 product-section 的后代的类型或属性。当您想要针对这些元素而不增加特异性时,这种技术是有用的——我们将在本章前面进一步讨论特异性。
属性选择器
顾名思义,属性选择器允许您基于属性或属性值的存在来定位元素。这让你可以做一些非常有趣和强大的事情。
例如,当您将鼠标悬停在具有 title 属性的元素上时,大多数浏览器都会显示工具提示。您可以使用这种行为来扩展事物的含义,如首字母缩写词和缩写词,由元素表示:
<p>The term <abbr title="self-contained underwater breathing apparatus">SCUBA</abbr> is an acronym rather than an abbreviation as it is pronounced as a word.</p>
但是,如果不将鼠标悬停在元素上,就无法知道这些额外的信息是否存在。为了解决这个问题,您可以使用属性选择器来设计标题不同于其他元素的 abbr 元素的样式——在本例中,通过给它们一个底部虚线边框。当光标悬停在元素上时,通过将光标从指针变为问号,可以提供更多的上下文信息,表明此元素不同于大多数元素。
abbr[title] {
border-bottom: 1px dotted #999;
}
abbr[title]:hover {
cursor: help;
}
除了根据属性的存在来设置元素的样式之外,还可以根据特定的值来应用样式。例如,这可以用来修复光标悬停在提交按钮上时浏览器显示的不一致性。有了下面的规则,所有类型属性值为 submit 的 input 元素在鼠标经过时都会显示一个手形指针:
input[type="submit"] {
cursor: pointer;
}
由于我们可能对属性值的模式感兴趣,而不是对确切的值感兴趣,所以属性选择器允许以更细粒度的方式匹配这些属性。通过在等号前添加一个特殊字符,我们可以指出我们感兴趣的匹配类型。
要匹配属性开头的值,请在等号前使用脱字符(^)。
a[href^="http:"]
要匹配属性末尾的值,请使用美元符号($)。
img[src$=".jpg"]
要匹配属性中任何位置的值,请使用星号(*)。
a[href*="/about/"]
要匹配以空格分隔的字符串列表中的值(如 rel 属性中的值),请使用波浪号字符(∩)。
a[rel∼=next]
还有一个属性选择器,可以选择值的开头匹配的元素,或者是单独匹配,或者后面紧跟一个破折号。对于这种匹配,请使用管道字符(|)。
a[hreflang|=en]
这个例子将匹配属性值 en 和 en-us,并提示使用这个选择器的意图:这对于在属性值中选择具有特定语言代码的元素很方便,因为它们是用破折号分隔的。从技术上讲,您可以将它与 class 属性一起使用来匹配类名,例如分别匹配 message 和 message-error,但是这样做的可移植性不是很好:如果您在 HTML 中的 message 类之前放置了另一个类,例如 class="box message ",那么选择器将不起作用。
伪元素
有时,您希望将页面的一部分作为目标,而该部分不是由元素表示的,但是您又不想在页面上添加额外的标记。CSS 为一些最常见的情况提供了一个简短的列表。这些被称为伪元素。
首先,您可以通过使用::first-letter 伪元素来定位一段文本的第一个字母。每段文本的第一行可以用::first-line 版本作为目标。
还有一些伪元素,分别使用::before 和::after 伪元素,对应于存在于一段内容的开头和结尾的假设元素。这对于插入小符号和印刷修饰非常有用,并且通常作为创建视觉效果的挂钩,否则您会附加到真实元素上。一种方法是使用 content 属性以文本的形式插入内容,但是可以随意使用背景、边框等来设置伪元素的样式,就像设置任何其他元素的样式一样。
警告
使用伪元素注入内容时要小心!不要用它们来添加任何形式的文本内容,如果你的 CSS 不能正确加载的话,你的用户就离不开它们。还要注意,屏幕阅读器没有一个标准的方法来解释伪元素的内容:有些人忽略它,有些人阅读它。
将这些伪元素放在一个例子中,我们可以用最少的标记得到类似图 2-3 的东西。
图 2-3。夏洛克·福尔摩斯小说的开头一段,的血字研究,借助伪元素做了一些排版处理
下面是实现这一点的 HTML 和 CSS 的缩略版本。
HTML:
<h1>A Study In Scarlet</h1>
<section class="chapter">
<p>In the year 1878 I took my degree of Doctor of Medicine of the University of London, and proceeded to Netley to go through the course prescribed for surgeons in the army. Having completed my studies there, I was duly attached to the Fifth Northumberland Fusiliers as Assistant Surgeon.</p>
</section>
CSS:
.chapter::before {
content: '”';
font-size: 15em;
}
.chapter p::first-letter {
float: left;
font-size: 3em;
font-family: Georgia, Times, "Times New Roman", serif;
}
.chapter p::first-line {
font-family: Georgia, Times, "Times New Roman", serif;
text-transform: uppercase;
}
正如您所看到的,我们使用::first-letter 伪元素在段落的开头创建了一个不同字体的首字下沉字母。第一行被转换成大写字母,并使用不同的字体和::first-line 伪元素。我们还使用::before 伪元素在章节容器中添加了一个装饰性的引号。所有这些都不需要添加任何多余的元素!确实方便。
我们将在第四章中仔细研究更多的印刷技术。
小费
伪元素应该使用我们到目前为止看到的双冒号语法,以区别于伪类,在下一节中您将看到伪类使用单冒号。然而,伪元素是在旧浏览器中用单冒号语法引入的,并且仍然以这种方式编写。因此,为了兼容起见,您仍然可以对一些伪元素使用单冒号语法,我们已经在本书中的示例中适当地这样做了。
伪类
有些情况下,您可能希望根据文档结构之外的内容来设置元素的样式,例如,超链接或表单元素的状态。这可以使用一个伪类选择器来完成。这些选择器以冒号(:)开头,用于定位应用它们的元素中的特定状态或关系。
一些最常见的伪类选择器可以用于样式化链接,如下所示,并且应该总是包含在针对最常见 HTML 元素的基本样式集中:
/* makes all unvisited links blue */
a:link {
color: blue;
}
/* makes all visited links green */
a:visited {
color: green;
}
/* makes links red on mouse hover, keyboard focus */
a:hover,
a:focus {
color: red;
}
/*...and purple when activated. */
a:active {
color: purple;
}
这些伪类选择器的顺序很重要。首先需要:link 和:visited 规则,然后是与用户交互相关的规则。当用户悬停在链接上或将键盘焦点给予链接时,: hover 和:focus 选择器将覆盖:link 和:visited,最后当用户单击或用键盘选择链接时,后跟:active。链接是交互式内容,默认情况下可以聚焦和激活。默认情况下,还有很多其他的交互元素,比如表单域和按钮,所以这些伪类也适用于它们。您还可以通过使用 JavaScript 使其他元素具有交互性。
最后,您可以对几乎任何元素使用:hover 伪类,但是请记住,像触摸屏和键盘这样的输入方法实际上没有悬停状态,所以不要对基本功能使用:hover。
目标和否定
另一个有用的伪类是:target,它匹配任何具有 ID 属性的元素,该属性当前在页面的 URL 哈希中表示。如果我们去example.com/blog/1/#comment-3
并在那个页面上找到一个标记为<的评论文章 class = " comment " id = " comment-3 ">…</文章>,我们可以使用以下规则用淡黄色背景突出显示评论:
.comment:target {
background-color: #fffec4;
}
现在,如果我们想要突出显示那个评论,但前提是它不是那些内容被隐藏的灰显的、被否决的评论中的一个呢?还有一个选择器,用来排除某些选择器。遇到否定伪类,或者:not()选择器!假设我们在标记为“否决”的注释上有一个特殊的类名,我们可以将规则改为:
.comment:target:not(.comment-downvoted) {
background-color: #fffec4;
}
除了伪元素和它本身之外,negation 伪类几乎可以与您放入括号中的任何类型的选择器一起工作。
结构伪类
CSS 3 引入了大量与文档结构相关的新伪类。其中最常见的是第个子选择器,它可以用来设置表格中交替行的样式:
tr:nth-child(odd) {
background: yellow;
}
这将使表格中的第一行以及随后的每一行都具有黄色背景。第 n 个子选择器充当一个函数,它可以接受许多不同的表达式作为参数。它将接受关键字 odd 和 even,如前面的示例所示。它也可以是表示目标元素的序号位置的数字,例如在下面的示例中,它将所有表格的第三行设置为粗体:
tr:nth-child(3) {
font-weight: bold;
}
当我们看到对数字表达式的支持时,事情开始变得有点复杂;例如:
tr:nth-child(3n+4) {
background: #ddd;
}
上一个表达式中的数字 4 与我们要定位的第一个元素的序号位置有关,在本例中是第四个表行。数字 3 与第一个元素之后的每个目标元素的顺序位置相关。所以在前面的例子中,第个子选择器将匹配表中的第四、第七、第十等行,如图 2-4 所示。为了更好地理解这里的数学,表达式中括号内的 n 被替换为一个数字,从零开始,然后增加 1,直到不再有匹配的元素。
图 2-4。一个表,其中的行使用:n-child(3n+4)设置样式。表达式以 n 为数字进行计算,从 0 开始,只要有匹配项,就增加 1
你可以用这些表达式做各种疯狂的事情。例如,您可以减去一个数字,而不是添加一个数字,因此我们可以这样做:n-child(3n-4)并得到不同的结果。这同样适用于 n 之前的数字,或者 n 本身——将它改为负数可以得到一些有趣的结果。例如,表达式:n-child(-n+3)将只选择前三个元素!
还有其他伪类选择器支持这些类型的表达式,如下所示:
:nth-last-child(N)
:n-last-child 选择器的操作方式与:n-child 选择器非常相似,只是它从最后一个子元素开始计数,而不是从第一个子元素开始计数。
回到 CSS 2.1 中,第一个孩子有一个伪元素,名为 first-child,因此,使用它可以更简单地编写第 n 个孩子(1)。三级选择器规范为最后一个孩子添加了一个对应的,名为(你猜对了):last-child,对应于:n-last-child(1)。还有:独生子女和独生子女。如果一个元素是特定元素类型的唯一子元素,则:only-of-type 选择器适用于该元素。通过使用这些伪类选择器,我们可以更好地定位特定类型的元素:
:nth-of-type(N)
:nth-last-of-type(N)
这两个伪类选择器的行为方式与:n-child 选择器相同,只是它们忽略(并且不计算)任何不属于它们所应用的元素类型的元素。这让我们有机会创建一些非常高效的模式,而不会将选择器过多地绑定到标记上。
巧妙使用结构伪类
仅仅使用结构化的伪类就可以做很多事情:当根据元素在文档中的位置和它们的环境来选择元素时,它们给了你很大的精确性。例如,可以根据某种类型的子元素的数量来选择项目,这使得可以根据项目的总数来设计网格列等样式。这是通过结合使用:n-last-of-type 伪选择器和:first-child 选择器来实现的。下面是一个匹配包含四个“列”的内容的示例,假设这些列具有相同的元素类型:
.column:nth-last-of-type(4):first-child,
.column:nth-last-of-type(4):first-child ∼ .column {
/* Rules for when there is exactly four .column elements */
}
当这个选择器匹配时,意味着从末尾算起的第四个元素也是第一个元素,因此有四个与。列类。我们还包括相邻兄弟选择器,以确保我们选择了所有其余的列。很整洁,是吧?
请注意,编号匹配不仅仅计算具有类名列的元素:它选择具有该类名的所有元素,然后根据它们具有相同的元素类型来计算项目。在选择器级别 4 规范中,有一个过滤匹配项的建议,使用 of 关键字,后跟括号内的选择器:
:nth-child(2 of .column):first-child {}
可悲的是,这种更有用的结构化伪类还没有在浏览器中广泛使用。
结构选择器通常有广泛的支持,但在 Internet Explorer 8 和更早的版本中却没有。如果您需要支持这些遗留浏览器,您可能希望将这种技术限制在小的增强上,而使用标记挂钩来定位整体布局模式的元素。
有关基于元素计数的样式的更多启发,请参见海登·皮克林的文章“CSS 的数量查询”(alistapart.com/article/quantity-queries-for-css
)。
形成伪类
有许多专门针对表单中元素的伪类。这些可以用来反映特定表单输入的状态,这取决于用户如何与它们交互。
例如,HTML5 为表单输入引入了几个新的属性,其中一些我们在第一章中看到了。这些新属性之一是必需属性:
<label for="field-name">Name: </label>
<input type="text" name="field-name" id="field-name" **required**>
如果您想在视觉上突出显示必填字段,使其对用户更明显,您可以使用:required 伪类来定位具有必填属性的表单元素,并使输入的边框具有不同的颜色(参见图 2-5 ):
图 2-5。使用:required 伪类为必填字段提供深色边框
input:required {
outline: 2px solid #000;
}
类似地,我们可以对那些没有所需属性的输入使用
input:optional {
border-color: #ccc;
}
我们还有伪类来帮助设计有效和无效字段的样式。如果 input 元素需要特定的有效类型,比如电子邮件地址,那么我们可以在 type 属性中使用 HTML5 中定义的一系列不同的输入类型:
<input type="email" />
然后,可以使用以下样式基于输入的当前值的有效性对该元素进行样式化(图 2-6 显示了无效输入的示例):
图 2-6。最后一个字段不是一个有效的电子邮件地址,并被赋予了一个明显不同的轮廓:invalid 伪类。在屏幕上,您会看到这个无效的电子邮件地址用红色标出
/* When the field contains a valid email address: */
input[type="email"]:valid {
border-color: green;
}
/* When the contents are not a valid email address: */
input[type="email"]:invalid {
border-color: red;
}
还有许多其他的表单伪类,例如:in-range 和:out-of-range,用于数字类型的目标输入;readonly 属性的只读输入;以及 read-write,用于没有 readonly 属性的输入。要了解这些伪类的更多信息,可以在developer.mozilla.org/docs/Web/CSS/Pseudo-classes
从 MDN 拿货。
瀑布
即使是中等复杂的样式表,也可能有两个或更多的规则针对同一个元素。CSS 通过一个被称为级联的过程来处理这种冲突,这个概念非常重要,以至于它的名字就叫级联样式表。级联通过给每个规则分配一个重要性来工作。作者样式表是由站点开发人员编写的,被认为是最重要的。用户可以通过浏览器设置应用他们自己的样式,这些样式被认为是次重要的。最后,您的浏览器或用户代理使用的默认样式表是最不重要的,因此您可以随时覆盖它们。为了给用户更多的控制权,他们可以通过将规则指定为!重要的是,即使规则标记为!作者认为重要。那个!重要注释添加到属性声明的末尾,在规则中使用时如下所示:
p {
font-size: 1.5em !important;
color: #666 !important;
}
让用户覆盖规则的原因是!重要的是支持特定的可访问性需求,比如允许有某种形式阅读障碍的用户使用中等对比度的用户样式表。
因此,级联按以下重要性顺序工作:
-
用户样式标记为!重要的
-
作者样式标记为!重要的
-
作者样式
-
用户样式
-
浏览器/用户代理应用的样式
然后根据选择器的具体程度对规则进行排序。具有更具体选择器的规则会覆盖那些不太具体的规则。如果两个规则同样具体,则最后定义的规则优先。
特征
为了计算规则的具体程度,每种类型的选择器都被赋予一个数值。然后,通过累加每个选择器的值来计算规则的特异性。不幸的是,特异性不是以 10 为基数计算的,而是一个很高的、未指定的基数,这意味着 10 个类选择器(或者 43 个)的特异性不等于或大于 1 个 ID 选择器。这是为了确保高度特定的选择器(如 ID)永远不会被许多不太特定的选择器(如类型选择器)覆盖。但是,如果一个特定规则中的选择器少于 10 个,为了简单起见,可以用 10 进制计算特异性。
选择器的特异性被分解成四个组成级别:a、b、c 和 d。
-
如果样式是内联样式,则 a 等于 1。
-
b 等于 ID 选择器的总数。
-
c 等于类、伪类和属性选择器的数量。
-
d 等于类型选择器和伪元素选择器的数量。
使用这些规则,可以计算任何 CSS 选择器的特异性。表 2-1 显示了一系列选择器,以及它们相关的特性。
表 2-1。特异性示例
|选择器
|
特征
|
碱基 10 的特异性
|
| — | — | — |
| Style= " " | 1,0,0,0 | One thousand |
| #包装#内容{} | 0,2,0,0 | Two hundred |
| #内容。发布日期{} | 0,1,1,0 | One hundred and ten |
| div #内容{} | 0,1,0,1 | One hundred and one |
| #内容{} | 0,1,0,0 | One hundred |
| p .评论。发布日期{} | 0,0,2,1 | Twenty-one |
| 页:1 | 0,0,1,1 | Eleven |
| div p {} | 0,0,0,2 | Two |
| p {} | 0,0,0,1 | one |
乍一看,所有这些关于特异性和高但未定义的基数的谈论似乎有点令人困惑,所以这里是你需要知道的。本质上,用样式属性编写的规则总是比任何其他规则更具体。有 ID 的规则比没有 ID 的规则更具体,有类选择器的规则比只有类型选择器的规则更具体。最后,如果两个规则具有相同的特征,由于级联生效,最后定义的规则优先。
注意
通用选择器(*)的特异性始终为 0,不管它在选择器链中出现多少次或出现在什么位置。稍后我们将在“继承”一节中展示一个例子,说明这是如何产生一些意想不到的结果的。
解析级联时的规则顺序
当两个规则具有相同的特性时,最后定义的规则优先,这是一个重要的事实。这意味着您必须考虑将规则放在样式表中的什么位置,以及选择器的顺序。
级联的一个很好的例子是在 link 元素上使用伪类,如前所述。因为每个选择器都有相同的特性,所以声明它们的顺序变得很重要。如果在 a:hover 选择器之后有 a:visited 选择器,一旦访问了链接,悬停样式就不会再出现,因为它被:visited 样式覆盖了。这看起来并不直观,直到你理解了特异性和级联的细节。记住链接伪类的顺序的一个方便的记忆方法是“维德勋爵讨厌毛茸茸的动物。”所以你应该从:link 伪类开始,然后是:visited,:hover,:focus,最后是:active。
管理特异性
理解特殊性对于编写好的 CSS 至关重要,这也是大型网站中最难控制和管理的方面之一。特异性允许您为公共元素设置常规样式,然后为更具体的元素覆盖它们。在下面的例子中,我们为不同类型的介绍性文本设置了一些规则。我们有一个灰色的基本介绍性文本颜色,覆盖了正文的默认黑色。在主页上,介绍文本是浅灰色背景的黑色,里面的链接是绿色的。
body {
color: black;
}
.intro {
padding: 1em;
font-size: 1.2em;
color: gray;
}
#home .intro {
color: black;
background: lightgray;
}
#home .intro a {
color: green;
}
这就引入了一系列对他们具有广泛针对性的规则。这在较小的网站上不太可能造成任何问题,但是随着代码库的增长和越来越多的样式影响页面,这种规则可能会变得难以管理,因为要对主页介绍文本应用任何进一步的规则,需要一个至少具有一个 ID 和一个类的选择器。
例如,假设我们有另一个组件,它有一个行动号召链接,通过简单地使用背景颜色和一些填充,它看起来有点像一个按钮:
a.call-to-action {
text-decoration: none;
background-color: green;
color: white;
padding: 0.25em;
}
如果我们想在主页简介中添加一个行动号召链接,会发生什么?好吧,说得委婉一点,这会看起来很糟糕,因为文本是不可见的:主页介绍中的链接选择器会覆盖我们的“按钮”样式,并在绿色背景上创建绿色文本(见图 2-7 )。
图 2-7。主页简介中的行动号召组件。介绍的链接样式(#home。intro a)比组件的样式(a.call-to-action)更具体,在绿色背景上给出绿色文本
为了减轻这一点,我们需要以某种方式增加特异性,可能是在行动号召组件上使用另一个更强大的选择器:
a.call-to-action,
#home .intro a.call-to-action {
text-decoration: none;
background-color: green;
color: white;
padding: 10px;
}
随着样式表的增长,必须像这样改进规则,这可能导致特异性军备竞赛,最终导致代码过于复杂。
更好的方法是简化您的选择器,减少它们的特异性:
body {
color: black;
}
.intro {
font-size: 1.2em;
color: gray;
}
.intro-highlighted {
color: black;
background: lightgray;
}
.intro-highlighted a {
color: green;
}
a.call-to-action {
text-decoration: none;
background-color: green;
color: white;
padding: 10px;
}
我们通过重写前面的代码做了两件事。首先,我们移除了 ID 选择器,这将这些选择器的特异性降到了最低。我们还删除了对简介的上下文的任何引用。我们没有在主页上谈论介绍,而是将主页介绍(改名为“高亮介绍”)作为原始介绍的一个更具体的版本。您现在可以像这样使用这些介绍类:
<p class="intro">A general intro</p>
<p class="intro intro-highlighted">We might need to use this on the homepage, or in the future, on a <a href="/promo-page" class="call-to-action">promo page</a>.</p>
这种更简单、更有针对性的方法让作者可以更好地控制自己的样式。突出显示的链接不再覆盖行动号召链接的颜色,您还可以在其他页面上重用突出显示的组件,而无需更改 CSS。
特异性和调试
在修复 bug 时,特殊性可能是极其重要的,因为您需要知道哪些规则优先以及为什么优先。例如,假设您有以下一组规则。快速浏览一下,你认为这两个标题会是什么颜色?
#content #main h2 {
color: gray;
}
div > #main > h2 {
color: green;
}
#content > [id="main"] .news-story:nth-of-type(1) h2.first {
color: hotpink;
}
:root [id="content"]:first-child > #main h2:nth-last-child(3) {
color: gold;
}
HTML:
<div id="content">
<main id="main">
<h2>Strange Times</h2>
<p>Here you can read bizarre news stories from around the globe.</p>
<div class="news-story">
<h2 class="first">Bog Snorkeling Champion Announced Today</h2>
<p>The 2008 Bog Snorkeling Championship was won by Conor Murphy
with an impressive time of 1 minute 38 seconds.</p>
</div>
</main>
</div>
令人惊讶的是,答案是两个标题都是灰色的。第一个选择器具有最高的特异性,因为它由两个 ID 选择器组成。一些后来的选择器可能看起来更复杂,但是因为它们只包含一个 ID,所以它们总是比不上更具体的选择器。值得注意的是,即使一些选择器包含对 HTML 的 ID 属性的引用,它们仍然只是属性选择器,并且具有较低的特异性。如果您只有 ID 属性来挂钩您的样式,并且不想让您的规则具有太高的特异性,那么这可能是一个有用的工具。
调试特殊性问题可能是一件棘手的事情,但幸运的是,有一些工具可以帮助您。所有现代浏览器都内置了开发人员工具,可以非常清楚地看出特定性是如何应用于特定元素的。在 Chrome 中,开发者工具(DevTools)允许你“检查一个元素”,并会列出所有的 CSS 选择器和与之匹配的规则,包括浏览器默认设置。图 2-8 显示了前一个示例代码中的第二个 h2,证明第二个标题实际上是灰色的,这是因为第一个最具体的选择器。
图 2-8。通过谷歌 Chrome 开发者工具,看看实际应用了哪些规则
遗产
人们经常混淆遗传和级联。虽然乍看之下,这两个概念似乎有关联,但实际上是完全不同的。幸运的是,继承是一个更容易理解的概念。某些属性(如颜色或字体大小)由应用这些样式的元素的后代继承。例如,如果您将 body 元素的文本颜色设置为黑色,那么 body 元素的所有后代也将具有黑色文本。字体大小也是如此。
如果您设置了正文的字体大小,您会注意到页面上的任何标题都不会选择这种样式。你可以假设标题不继承文本大小。但实际上是浏览器默认样式表设置了标题大小。任何直接应用于元素的样式都会重写继承的样式。这是因为继承的样式具有空特性。
继承是非常有用的,因为它让您不必为元素的每个后代添加相同的样式。如果您试图设置的属性是一个继承属性,您也可以将其应用于父元素。毕竟,写作的意义是什么:
p, div, h1, h2, h3, ul, ol, dl, li {color: black;}
当你可以写下面的内容时。
body {color: black;}
继承的属性值根本没有特异性,甚至为零。这意味着通过通用选择器设置的属性(其特异性为零)将覆盖继承的属性。这给了我们图 2-9 所示的可能意想不到的情况,其中通用选择器设置的颜色覆盖了从标题继承的颜色:
图 2-9。通用选择器的特异性为 0,但它仍然优于继承属性
* {
color: black;
}
h2 {
color: red;
}
<h2>The emphasized text will be <em>black</em></h2>
相反,在 body 元素上设置一个基本颜色对于这种情况来说是一个更好的选择,所以这个颜色是继承的,而不是为所有其他元素设置的。
正如明智地使用级联可以帮助简化 CSS 一样,良好地使用继承可以帮助减少代码中选择器的数量和复杂性。如果您有许多继承不同样式的元素,那么确定样式的来源可能会变得很混乱。
将样式应用到您的文档
当你在写 CSS 的时候,你需要知道如何将这些样式应用到一个给定的 HTML 文档中。有各种方法可以做到这一点,每种方法都有自己的优点和缺点。
链接和样式元素
通过将样式放置在 style 元素中,可以直接将样式添加到文档的标题:
<style>
body {
font-family: Avenir Next, SegoeUI, sans-serif;
color: grey;
}
</style>
如果您有少量的样式想要立即应用到页面,并且您不希望浏览器下载单独的文件的开销,有时这很有用。但是,您通常希望应用外部样式表中的样式,以便在其他页面中重用。有两种方法可以将外部样式表附加到网页。最常见的方法是使用 link 元素:
<link href="/c/base.css" rel="stylesheet" />
这将指导浏览器下载 base.css 文件,并将其包含的任何样式应用于页面。您可以将它添加到任意数量的 HTML 页面中,因此这是跨多个页面甚至跨多个站点重用一组样式的好方法。
您还可以使用@import 指令来加载外部 CSS 文件:
<style>
@import url("/c/modules.css");
</style>
@import 指令可以用在 HTML 文档头部的样式块中,也可以用在外部样式表本身中。后一种用法意味着在页面上包含一个外部 CSS 文件可能会导致浏览器加载后续的 CSS 文件。
从表面上看,使用 link 元素或@import 指令可以获得几乎相同的结果,但是有一些重要的考虑因素使 link 优于@import,我们将在下一节的性能中讨论这些因素。
当向页面添加样式时,不要忘记顺序对层叠很重要:当两个或多个具有相同特征的规则在设置元素的属性上竞争时,最后声明的规则获胜。
当您向 HTML 添加几个引用样式表的链接元素或添加样式元素时,它们在声明顺序方面的位置由它们在 HTML 源代码中的顺序决定。考虑来自 HTML 元素头部的以下片段,其中所有引用的样式表和样式元素为 h1 元素声明了不同的颜色,具有相同的特异性:
<link rel="stylesheet" href="css/sheet1.css">
<style>
@import 'css/sheet3.css';
h1 {
color: fuchsia;
}
</style>
<link rel="stylesheet" href="css/sheet2.css">
在这种情况下,声明的顺序如下:
-
来自 sheet1.css 的声明
-
sheet3.css 中的声明,在 style 元素中导入
-
style 元素内部的声明
-
来自 sheet2.css 的声明
胜出的声明将是 sheet2.css 中的声明,因为它是列表中的最后一个。
表演
选择哪种方式将 CSS 加载到页面中是控制浏览器显示页面速度的最大选择(假设 HTML 页面本身加载速度很快!).
web 性能的一个重要指标是内容开始显示在屏幕上所需的时间。这有时被称为“渲染时间”或“玻璃时间”
现代浏览器在开始在屏幕上呈现内容之前至少需要两样东西:HTML 和 CSS。这意味着让浏览器尽快下载 HTML 和所有 CSS 是非常重要的。
不要试图通过将 CSS 放在主体中或靠近页脚来延迟它的加载。当浏览器预先拥有了布局页面所需的所有 CSS 信息时,它们的反应最好。这样,他们就可以开始理解页面的外观,并一次性将页面呈现到屏幕上,而不是不断地在加载新样式时进行调整。
减少 HTTP 请求
当链接到外部样式表时,将文件数量保持在最小是很重要的。每个额外的文件都会导致一个额外的 HTTP 请求,从服务器请求文件的行为会大大增加浏览器下载和应用所有样式的时间开销。额外的 HTTP 请求意味着从浏览器发送额外的数据,比如 cookies 和请求头。然后,服务器必须为每个请求发回响应头。两个文件总是比一个具有相同 CSS 内容的文件在浏览器和服务器之间发送更多的数据。
总是试着把你在一个实时网站上发布的 CSS 文件的数量保持在一到两个。只使用一个 link 元素加载 CSS 文件,然后在其中使用@import,这并不意味着浏览器只使用一个请求:相反,这意味着它需要一个对链接文件的请求,然后是获取所有导入文件的后续请求。所以避免使用@import(至少在直播网站上)。
压缩和缓存内容
同样非常重要的是,你在现场使用的任何文件都是用 GZIP 压缩的。CSS 压缩非常有效,因为它有许多重复的模式,如属性名和值。在许多情况下,可以将 CSS 文件的大小减少 70–80 %,这可以显著减少用户的带宽和加载时间。大多数 web 服务器都有一种机制,可以将内容自动压缩到支持它的浏览器中。
同样,指导 web 服务器为 CSS 文件设置适当的缓存时间也很重要。理想情况下,你希望用户的浏览器下载一次 CSS 文件,并且在它改变之前不再下载。这方面的策略包括设置各种 HTTP 头,指示浏览器将文件缓存很长时间,然后在有任何变化时通过更新文件名来“破坏缓存”。
这是如何工作的细节已经超出了本书的范围。您可能需要您的主机提供商或您公司的系统管理员的支持来帮助配置服务器,但是压缩和正确缓存内容是您可以做的提高网站性能的两件最重要的事情。
避免呈现阻塞 JavaScript
当您在 HTML 文档的元素中添加一个
这导致了在 HTML 页面的底部,紧接在结束的
<!-- Load scripts last -->
<script src="/scripts/core.js"></script>
</body>
一种更现代的方法是在中使用
<head>
<!-- will load asynchronously, but execute immediately when downloaded -->
<script src="/scripts/core.js" async></script>
<!-- will load asynchronously, but execute after HTML is done-->
<script src="/scripts/deferred.js" defer></script>
</head>
通过使用这两种方法加载 JavaScript,您可以确保浏览器能够解析和显示 HTML 内容和 CSS,而不会因 JavaScript 文件请求而延迟。选择哪种方法主要是浏览器支持的问题:async 和 defer 属性是 HTML5 标准的一部分,因此是较新的。最明显的是,10 版之前的 Internet Explorer 缺少或部分支持。
摘要
在这一章中,你已经重新熟悉了常见的 CSS 选择器,并且了解了一些你以前可能没有遇到过的强大的新选择器。现在,您对特异性如何工作以及如何使用级联来构建 CSS 规则并帮助它们达到目标有了更好的理解。我们已经初步了解了如何避免陷入特异性军备竞赛,以及如何利用你对特异性、级联和遗传的理解来获得优势。您还了解了如何将 CSS 应用于文档,以及这会影响网页性能的一些方式。
在下一章,你将学习 CSS 盒子模型,边距如何以及为什么折叠,以及浮动和定位是如何工作的。
三、可视化格式模型概述
一些需要掌握的最重要的 CSS 概念是浮动、定位和盒子模型。这些概念控制元素在页面上的排列和显示方式,并构成许多布局技术的基础。最近,专门为控制布局而设计的新标准已经被引入,我们将在接下来的章节中单独讨论这些标准。但是,您在本章中学习的概念将帮助您完全掌握盒模型的复杂性、绝对和相对定位之间的差异以及浮动和清除实际上是如何工作的。一旦你牢牢掌握了这些基础知识,使用 CSS 开发网站就变得容易多了。
在本章中,您将了解:
-
盒子模型的复杂性
-
利润崩溃的方式和原因
-
不同的定位属性和值
-
浮动和清算是如何工作的
-
什么是格式化上下文
盒子模型概述
盒子模型是 CSS 的基石之一,它决定了元素是如何显示的,并且在一定程度上决定了元素之间的交互方式。页面上的每个元素都被认为是一个由元素内容、填充、边框和边距组成的矩形框(见图 3-1 )。
图 3-1。盒子模型的插图
内容区域周围应用了填充。如果向元素添加背景,它将应用于由内容和填充形成的区域。因此,填充通常用于在内容周围创建一个装订线,使其看起来不会与背景边缘齐平。添加边框会在填充区域的外部应用一条线。这些线条有多种样式,如实线、虚线或虚线。边框之外是边距。边距是框的可见部分之外的透明空间,允许您控制页面中元素之间的距离。
另一个可以应用于框但不影响其布局的属性是 outline 属性,它在元素的边框周围绘制一条线。它不影响框的宽度或高度,在调试复杂布局或演示布局效果时会很有用。
填充、边框和边距是可选的,默认值为零。然而,用户代理样式表将为许多元素提供边距和填充。例如,默认情况下,标题总是有一些边距,尽管这些边距因浏览器而异。您当然可以在自己的样式表中覆盖这些浏览器样式,或者在特定的元素上,或者通过使用一个重置的样式表,如第二章所讨论的。
盒子尺寸
默认情况下,框的宽度和高度属性指的是内容框的宽度和高度——由元素的呈现内容的边缘形成的矩形。添加边框和填充不会影响内容框的大小,但会增加元素框的整体大小。如果您希望一个边框为 5 像素、两边填充为 5 像素的框的总宽度为 100 像素,则需要将内容的宽度设置为 80 像素,如下所示。如果盒子周围也有 10 像素的边距,它将占据总共 120 像素宽的空间(见图 3-2 )。
图 3-2。默认的盒子模型。width 属性适用于内容区域
.mybox {
**width: 80px;**
padding: 5px;
border: 5px solid;
margin: 10px;
}
您可以使用 box-sizing 属性更改计算框宽度的方式。框大小的默认值是 content-box,并应用到目前为止描述的行为。然而,让宽度和高度属性不仅仅影响内容框是非常有用的,尤其是在响应式布局中。
注意
在某些浏览器中,某些表单控件元素(如 input)可能有不同的框大小默认值。这是由于与传统行为的兼容性,在传统行为中,不可能更改填充或边框等内容。
如果将 box-sizing 属性的值设置为 border-box,如下所示,那么 width 和 height 属性将包括框的填充和边框所需的空间(参见图 3-3 )。边距仍然影响元素在页面上占据的总大小,但仍然不包括在宽度中。使用这些规则,您可以实现如图 3-2 所示的整体布局:
图 3-3。当 box-sizing 属性设置为 border-box 时的框模型。width 属性现在对应于元素可见部分的整个宽度
.mybox {
box-sizing: border-box;
**width: 100px;**
padding: 5px;
border: 5px;
margin: 10px;
}
那么这为什么有用呢?从很多方面来说,这是一种更直观的处理盒子的方式,事实上,在 IE6 之前的旧版本的 Internet Explorer 中,盒子模型就是这样工作的。它是“直观的”,因为当你仔细想想,这就是盒子在现实世界中的工作方式。
想象一个 CSS 盒子就像一个包装箱。盒子的壁作为一个边界,并提供视觉定义,而填充物在里面保护内容。如果框需要特定的宽度,添加更多的填充或增加墙的厚度会侵蚀可用的内容空间。现在,如果您需要在堆叠盒子之前将它们隔开,每个盒子之间的空间(实际上是边距)对盒子本身的宽度没有影响,或者实际上对可用内容空间的大小没有影响。这感觉像是一个更合理的解决方案,所以很遗憾浏览器开发者,包括微软在 IE 后续版本中,决定走不同的方向。
幸运的是,box-sizing 属性允许我们覆盖默认行为并简化 CSS 布局中的一些常见模式。举以下例子:
<div class="group">
<article class="block">
</article>
</div>
如果我们想确保任何。块在我们的。组始终是其包含列宽度的三分之一,我们可以应用以下规则:
.group .block {
width: 33.3333%;
}
这将工作得很好,直到我们开始添加水槽使用 adding 对我们的边。块,使其内容远离可见边缘。现在我们的。块元素是父元素的三分之一。group 元素的宽度加上填充,这可能会破坏我们想要的布局。图 3-4 说明了不同之处。
图 3-4。假设我们想要。块元素是。组元素,当我们向它添加填充时,可能会得到意外的结果
例如,我们可以通过添加额外的内部元素来解决这个问题,或者我们可以选择不同的框大小属性来改变宽度的计算方式(参见图 3-5 ):
图 3-5。添加框尺寸:即使添加了填充,border-box 也将我们的框保持在 33.3333%的宽度
.group .block {
width: 33.3333%;
**box-sizing: border-box;**
**padding: 20px;**
}
现在我们的。block 元素的宽度正好是父元素宽度的三分之一,正如我们声明的那样,无论我们向它添加多少填充或边框。
填充、边框和边距可以应用于元素的所有边或单个边。边距也可以为负值。这可以以多种有趣的方式用于将元素拉入或拉出页面的位置。我们将在后面的章节中探讨这些技术。
您可以使用 CSS 规范中的任何长度度量(如像素、ems 和百分比)来为元素添加填充和边距。使用百分比值有一些值得一提的特点。假设加价和上一个例子中的一样,那么这个例子中的 5%实际上代表了什么?
.block {
margin-left: 5%;
}
答案是,在这种情况下,它是父项宽度的 5%。组元素。如果我们假设。组元素的宽度为 100 像素,它的左边有 5 像素的边距。
当使用这些尺寸来测量元素顶部和底部的填充或边距时,猜测百分比是从父元素的高度得出的是情有可原的。这乍一看似乎很合理——然而,由于高度通常没有声明,并且可以随着内容的高度而变化,CSS 规范规定填充和边距的顶部和底部值也从包含块的的宽度中取值。在这种情况下,包含块是父块,但是这是可以改变的——我们将在本章的前面一点说明这意味着什么。
最小值和最大值
有时,对元素应用最小宽度和最大宽度属性可能很有用。这样做在实践响应式设计时特别有用,因为它允许块级框默认情况下自动填充其父元素的宽度,但不会收缩到小于 min-width 中指定的值,也不会增长到大于 max-width 中指定的值。(我们将在第八章回到响应式网页设计,以及它与 CSS 的关系。)
类似地,也存在最小高度和最大高度属性,尽管在 CSS 中应用任何高度值时应该小心,因为元素从它们包含的内容中隐式地导出它们的高度几乎总是更好。否则,如果内容量增加或文本大小改变,内容可能会流出固定高度的框。如果你因为某种原因需要设置一个默认的高度,使用最小高度通常会更好,因为它可以让你的框随着内容扩展。
可视化格式模型
了解了盒子模型之后,我们可以开始探索一些可视化的格式和定位模型。
人们通常将 p、h1、article 等元素称为块级元素。这意味着它们是可视地显示为内容块或块框的元素。相反,诸如 strong、span 和 time 之类的元素被描述为内联级元素,因为它们的内容在行内显示为内联框。
使用 display 属性可以更改生成的框的类型。这意味着您可以通过将 span 等内联级元素的 display 属性设置为 block,使其行为类似于块级元素。通过将元素的 display 属性设置为 none,也可以使元素根本不生成任何框。该框以及所有内容不再显示,也不占用文档空间。
CSS 中有许多不同的定位模型,包括浮动、绝对定位和相对定位。除非指定,否则所有盒开始时都位于正常流中,并具有默认的静态属性。顾名思义,元素框在正常流中的位置将由元素在 HTML 中的位置决定。
块级框将一个接一个地垂直出现;框之间的垂直距离由框的垂直边距计算。
内联框水平排列成一行,跟随文本流向,当文本换行时换行。可以使用水平填充、边框和边距来调整它们的水平间距(参见图 3-6 )。但是,垂直填充、边框和边距对内嵌框的高度没有影响。类似地,在内联框上设置显式的高度或宽度也不会有任何效果。
图 3-6。段落块框内的内嵌组件
由一行文本形成的水平框被称为行框,一个行框对于它可能包含的所有行内框来说总是足够高。改变行框尺寸的唯一方法是更改行高,或者设置行框内任何行框的水平边框、填充或边距。图 3-6 显示了具有两行文本的段落的块框,其中一个单词在内联显示的< strong >元素内。
还可以将元素的 display 属性设置为 inline-block。顾名思义,该声明使元素水平排列,就像它是一个行内框一样。但是,盒子内部的行为就好像盒子是块级的,包括能够显式设置宽度、高度、垂直边距和填充。
当您使用表格标记(table、tr、th 和 td 元素等)时,表格本身表现为一个块,但是表格的内容将根据生成的行和列进行排列。还可以设置其他元素的 display 属性,以便它们采用表格的布局行为。通过以正确的方式应用值 table、table-row 和 table-cell,您可以实现 HTML 表格的一些属性,而无需在标记中使用表格。
像 Flexible Box Layout(也称为 flexbox )和 Grid Layout(我们将在后面的章节中介绍)这样的模块进一步扩展了显示属性。通常,这些新的布局模式创建的框在其外部环境中充当块,但创建了如何处理框内内容的新规则。
外部和内部显示模式之间的这种划分(在内嵌块、表格和新值(如 flex 或 grid)中都可以看到)现在正在显示级别 3 模块中进行标准化。在那里,显示模式的现有属性和关键字被扩展,以允许更细粒度的控制。重要的一点是,行内级别的框和块级别的框仍然是 HTML 元素默认行为的基础,但实际情况稍有不同。
匿名信箱
与 HTML 元素可以嵌套的方式一样,框可以包含其他框。大多数盒子都是由明确定义的元素组成的。但是,有一种情况,即使没有显式定义块级元素,也会创建块级元素—当您在块级元素(如 section)的开头添加一些文本时,如下所示。即使您没有将“some text”位定义为块级元素,它也会被视为块级元素。
<section>
some text
<p>Some more text</p>
</section>
在这种情况下,盒子被描述为一个匿名 块 盒子,因为它不与一个特别定义的元素相关联。
块级元素中的文本行框也会发生类似的情况。假设您有一个包含三行文本的段落。每行文本形成一个匿名行框。您不能直接设置匿名块框或行框的样式,除非通过使用:first-line 伪元素,该元素显然用途有限,仅允许您更改与版式和颜色相关的某些属性。然而,理解你在屏幕上看到的一切都创造了某种形式的盒子是很有用的。
边距折叠
对于普通的块盒,有一种行为被称为边距折叠。边距折叠是一个相对简单的概念。然而,在实践中,当你设计一个网页时,它会引起很多混乱。简单来说,当两个或两个以上的垂直边距相遇时,它们将折叠形成一个单独的边距。此边距的高度将等于两个折叠边距中较大的一个的高度。
当两个元素在另一个之上时,第一个元素的底边将与第二个元素的上边一起折叠(见图 3-7 )。
图 3-7。元素的上边距与前一个元素的下边距一起折叠的示例
当一个元素包含在另一个元素中时,假设没有填充或边界分隔边距,它们的顶部和/或底部边距也将折叠在一起(参见图 3-8 )。
图 3-8。元素的上边距与其父元素的上边距一起折叠的示例
乍一看,这似乎很奇怪,但利润率甚至会自行下降。假设您有一个空元素,它有边距,但没有边框或填充。在这种情况下,上边距接触下边距,它们一起折叠(见图 3-9 )。
图 3-9。元素的上边距与其下边距一起折叠的示例
如果此页边空白接触到另一个元素的页边空白,它将自行折叠(见图 3-10 )。
图 3-10。一个空元素的折叠边距与另一个空元素的边距一起折叠的示例
这就是为什么一系列空的段落元素占用很少的空间,因为它们所有的边距都折叠在一起形成一个小的边距。
利润率下降乍一看似乎很奇怪,但实际上很有意义。取一个由几个段落组成的典型文本页面(见图 3-11 )。第一段上方的空间将等于段落的上边距。如果没有边距折叠,所有后续段落之间的间距将是它们两个相邻的上边距和下边距的总和。这意味着段落之间的间距将是页面顶部间距的两倍。随着页边距的折叠,每个段落之间的顶部和底部页边距也会折叠,使间距与其他地方的间距相同。
图 3-11。边距折叠以保持元素之间的间距一致
在文档的正常流动中,边距折叠仅发生在块框的垂直边距上。内联框、浮动框或绝对定位框之间的边距永远不会缩小。
包含块
赋予元素包含块的的概念很重要,因为它决定了如何解释各种属性,就像我们前面看到的用百分比设置填充和边距的情况。
元素的包含块取决于元素的定位方式。如果元素有一个静态位置(与没有声明 position 属性相同)或相对位置,则它的包含块被计算到其最近的父元素的边缘,该父元素的 display 属性被设置为导致类似块的上下文的内容,包括 block、inline-block、table-cell、list-item 等。
默认情况下,当以百分比设置时,宽度、高度、边距和填充的声明是根据父元素的尺寸计算的。当您将元素更改为具有绝对或固定的定位模型时,这种情况会发生变化。接下来,我们将介绍不同的模型,以及它们如何与包含块的概念进行交互。
相对定位
当您将元素的 position 属性设置为 relative 时,它最初将完全停留在原来的位置。然后,通过使用 top、right、bottom 和 left 属性设置垂直或水平位置,可以相对于元素的起点移动元素。如果您将顶部位置设置为 20 像素,该框将出现在其原始位置顶部下方 20 像素处。将左侧位置设置为 20 像素,如下所示,将在元素左侧创建一个 20 像素的空间,将元素向右移动(参见图 3-12 )。
图 3-12。相对定位元素
.mybox {
position: relative;
left: 20px;
top: 20px;
}
使用相对定位,无论是否偏移,元素都会继续占据页面流中的原始空间。因此,偏移元素会导致它与其他框重叠。
绝对定位
相对定位实际上被认为是正常流程定位模型的一部分,因为元素是相对于其在正常流程中的位置的。相比之下,绝对定位将元素从文档流中移除,因此不占用空间。文档正常流程中的其他元素将表现为绝对定位的元素从未出现过(见图 3-13 )。
图 3-13。绝对定位元素
绝对定位元素的包含块是其最近的定位祖先,这意味着 position 属性设置为 static 以外的任何值的任何祖先元素。如果元素没有定位的祖先,它将相对于文档的根元素 html 元素定位。这也被称为初始包含块。
与相对定位的框一样,绝对定位的框可以从其包含块的顶部、底部、左侧或右侧偏移。这给了你很大的灵活性。您可以将一个元素放在页面上的任何位置。
因为绝对定位的框被从文档流中取出,所以它们可能会与页面上的其他元素重叠。您可以通过设置一个名为 z-index 的数值属性来控制这些框的堆叠顺序。z 索引越高,盒子在堆栈中的位置就越高。当用 z-index 堆叠物品时,有各种各样的复杂情况需要考虑:我们将在第六章中对它们进行分类。
尽管绝对定位是布局页面元素的有用工具,但它很少用于创建高级布局。绝对定位的框不参与文档的流动,这使得创建适应和响应不同宽度和不同长度内容的视口的布局变得相当麻烦。网络的本质不允许我们精确地测量元素在页面上的位置。随着我们越来越精通 CSS 中的其他布局技术,绝对定位在页面布局中的使用已经变得相当少见了。
固定定位
固定定位是绝对定位的一个子类。不同之处在于固定元素的包含块是视口。这使您可以创建始终停留在窗口中相同位置的浮动元素。许多网站使用这种技术,通过将它们固定在侧栏或顶栏中的位置,使它们的导航部分始终可见(图 3-14 )。这有助于提高可用性,因为用户不必看很远就能回到界面的重要部分。
图 3-14。当你向下滚动时,谷歌开发者文档的顶部栏和侧边导航保持不变
浮动的
另一个重要的视觉模型是浮动模型。浮动框可以向左或向右移动,直到其外边缘接触到其包含块或另一个浮动框的边缘。因为浮动框不在正常的文档流中,所以正常文档流中的块框几乎就像浮动框不存在一样。我们一会儿会解释“几乎”。
如图 3-15 所示,当您向右浮动框 1 时,它被从文档流中取出并向右移动,直到其右边缘接触到包含块的右边缘。它的宽度也会收缩到包含其内容所需的最小宽度,除非您通过设置特定的宽度或最小宽度/最大宽度明确地告诉它。
图 3-15。元素右浮动的示例
在图 3-16 中,当您向左浮动框 1 时,它被从文档流中取出并向左移动,直到其左边缘接触到包含块的左边缘。因为它不再在流中,所以它不占用空间,实际上位于盒子 2 的顶部,从视图中隐藏了它。如果将所有三个框都向左浮动,框 1 将向左移动,直到接触到其包含的块,另外两个框将向左移动,直到接触到前一个浮动的框。
图 3-16。向左浮动的元素示例
如果包含元素对于所有浮动元素来说太窄,以至于不能水平放置,那么剩余的浮动将下降,直到有足够的空间(参见图 3-17 )。如果被浮动的元素有不同的高度,当它们落下时,浮动可能会被其他浮动“卡住”。
图 3-17。如果没有足够的可用水平空间,浮动的元素将向下移动,直到有足够的水平空间
线盒和清除
在上一节中,您了解了浮动一个元素会将它从文档流中移除,不再对非浮动项目产生影响。实际上,这并不完全正确。如果文档流中的一个浮动元素后面跟着一个元素,则该元素的框将表现为好像该浮动元素不存在一样。但是,框中的文本内容保留了浮动元素的一些内存,并移开以腾出空间。在技术术语中,浮动元素旁边的行框被缩短,以便为浮动元素腾出空间,从而围绕浮动框流动。事实上,创建浮动是为了让文本围绕图像流动(见图 3-18 )。
图 3-18。行框在靠近浮动时会变短
要阻止线条框环绕浮动框的外部,需要对包含这些线条框的元素应用 clear 属性。clear 属性可以是 left、right、both 或 none,它指示框的哪一侧不应该靠近浮动框。许多人认为 clear 属性只是删除了一些否定先前浮动的标志。然而,现实要有趣得多。当你清除一个元素时,浏览器会在元素的顶部添加足够的边距,将元素的上边框边缘垂直向下推,越过浮动(见图 3-19 )。当您尝试将自己的边距应用于“已清除”的元素时,这有时会令人困惑,因为该值只有在达到并超过浏览器自动添加的值时才会生效。
图 3-19。清除元素的上边距,为前面的浮动创建足够的垂直空间
正如您所看到的,浮动元素被从文档流中取出,除了将行框缩短到足以为浮动框腾出空间之外,对周围的元素没有任何影响。但是,清除一个元素实质上是为所有前面的浮动元素清除一个垂直空间。这在使用浮动作为布局工具时很有用,因为它允许周围的元素为浮动的元素腾出空间。
让我们看看如何使用浮动创建一个简单的组件布局。假设您有一张图片,您希望它浮动在标题的左侧,一小块文本浮动在标题的右侧,这通常被称为“媒体对象”,因为常见的模式是有一段媒体(如图形、图像或视频)和一段伴随的文本。您希望此图片和文本包含在另一个具有背景颜色和边框的元素中。你或许可以试试这样的东西:
.media-block {
background-color: gray;
border: solid 1px black;
}
.media-fig {
float: left;
width: 30%; /* leaves 70% for the text */
}
.media-body {
float: right;
width: 65%; /* a bit of "air" left on the side */
}
<div class="media-block">
<img class="media-fig" src="/img/pic.jpg" alt="The pic" />
<div class="media-body">
<h3>Title of this</h3>
<p>Brief description of this</p>
</div>
</div>
但是,因为浮动元素是从文档流中取出的,所以具有。media-block 不占用空间,它只有浮动的内容,因此在文档流中没有给它一个高度。如何可视化地让包装器包含浮动的元素?您需要在该元素内部的某个地方应用 clear,正如我们前面看到的,这在被清除的元素上创建了足够的垂直边距,以便为浮动的元素留出空间(参见图 3-20 )。不幸的是,由于示例中没有要清除的现有元素,您可以在结束 div 标记之前添加一个空元素,并清除:
图 3-20。添加一个清除 div 会强制容器包含浮动
/* Added CSS: */
.clear {
clear: both;
}
<div class="media-block">
<img class="media-fig" src="/img/pic.jpg " alt="The pic" />
<div class="media-body">
<h3>Title of this</h3>
<p>Brief description of this</p>
</div>
<div class="clear"></div><!-- added extra empty div -->
</div>
这得到了我们想要的结果,但代价是在我们的标记中添加了额外的代码。通常会有一个现有的元素,您可以对其应用 clear,但有时您可能不得不硬着头皮为布局的目的添加无意义的标记。然而,在这种情况下,我们可以做得更好。
我们可以这样做的方法是使用:after 伪元素模拟额外的 clearing 元素,如下所示。通过将此应用到浮动元素的包含元素,将创建一个额外的框,您可以将清除规则应用到该框。
.media-block:after {
content: " ";
display: block;
clear: both;
}
这种方法和它的一些变体在尼古拉斯·加拉格尔的一小段代码中得到了最好的展示,这段代码被称为 micro clearfix,在nicolasgallagher.com/micro-clearfix-hack/
展示。
格式化上下文
CSS 有许多不同的规则,适用于元素在页面上水平或垂直流动时如何相互交互。这些规则集之一的技术名称是格式化上下文。我们已经看到了内联格式上下文的一些规则——例如,垂直边距对内联框没有影响。类似地,某些规则适用于块盒如何堆叠,就像我们在折叠边距一节中看到的那样。
其他规则定义了页面必须如何自动包含任何在末尾突出的浮动(否则浮动元素内的内容可能会在可滚动区域之外结束),并且默认情况下所有块框的边缘都与包含块的左边缘对齐(或右边缘,取决于文本方向)。这组规则被称为块格式化上下文。
一些规则允许元素建立自己的内部块格式上下文。其中包括以下内容:
-
元素的 display 属性被设置为一个值,该值为元素的内容创建一个类似块的上下文,如 inline-block 或 table-cell。
-
float 属性不是 none 的元素。
-
绝对定位的元素。
-
将 overflow 属性设置为 visible 以外的任何内容的元素。
正如我们之前讨论的,块的边缘接触其包含块的边缘的规则甚至适用于前面有浮动的内容。浮动从页面流中移除,并通过触发跟随它的元素中的行框来缩短,从而产生为自己腾出空间的视觉效果。元素本身仍然在浮动之下伸展到它需要的程度。
当一个元素有触发一个新的块格式上下文的规则,并且在一个 float 旁边时,它将忽略这条规则,即它的边缘必须靠着它包含的块的边。相反,它会缩小以适应——不仅仅是线路盒,而是整个系统。这可用于重新创建。上一节中的媒体块示例,但规则更简单:
.media-block {
background-color: gray;
border: solid 1px black;
}
.media-fig {
float: left
margin-right: 5%;
}
.media-block, .media-body {
overflow: auto;
}
<div class="media-block">
<img class="media-fig" src="/img/pic.jpg" alt="The pic" />
<div class="media-body">
<h3>Title of this</h3>
<p>Brief description of this</p>
</div>
</div>
在设置溢出:自动;在两个容器上。媒体块和我们的。媒体主体元素,我们为它们建立了新的块格式化上下文。这有几个影响(见图 3-21 中的比较):
-
它包含在中的浮动图像。无需清除规则的媒体块组件,因为块格式化上下文也自动包含浮动。
-
作为一个额外的好处,它允许我们抛弃宽度的规则以及。media-body 元素,如果我们需要的话——它将简单地调整到浮动旁边的剩余空间,并且仍然在图像旁边保持一个漂亮的直边。如果没有新的格式上下文,并且文本有点长,则下面的任何行框都会浮动。media-fig 将在它的下面伸展,最后在图像的左边齐平。
图 3-21。要是。media-fig 元素是浮动的,并且文本足够长,一些行将在浮动下换行并结束于左侧。创建新的块格式化上下文强制。要收缩的媒体主体
创建尽可能具有可预测和简单行为的布局降低了代码的复杂性,增加了布局的健壮性,因此知道何时应用这样的技巧来避免浮动和清除元素之间的复杂交互是一件好事。幸运的是,更好的布局技术正在迅速普及。
内在和外在规模
CSS 模块“内部和外部大小调整级别 3”定义了一个关键字列表,可以应用于(最小和最大)宽度和高度属性,而不是以像素或百分比等为单位的长度。这些表示从周围上下文(外部)或元素内容(内部)派生的显式长度,但让浏览器计算出最终值,而不是将属性设置为 auto 或使用 floats 或 block 格式上下文创建收缩到适合的场景而根本不设置宽度的隐式值。
我们不会在这里深入讨论不同关键字的细节,但是有趣的是,我们在其中发现了包含浮动。这个关键字应该和你预期的差不多;例如,您可以使用以下代码使元素包含任何浮点:
.myThing {
min-height: contain-floats;
}
到目前为止,对这个模块中各种关键字的支持还很弱——最值得注意的是,在撰写本文时,没有任何版本的 IE 支持其中的任何一个。尽管如此,它在未来创建健壮的规模估算时可能非常有用,而不需要求助于更复杂的技术。
其他 CSS 布局模块
我们已经讨论了 CSS 可视化格式模型的基础和最常见的部分,但是还有一些其他的地方需要简单的提一下。
您可能会认为健壮灵活的布局模型是 CSS 这样的可视化表示工具的关键部分。你是对的。但不幸的是,我们花了很长时间才得到一个。从历史上看,我们使用语言中任何可用的特性来实现我们的目标,即使它们远不是工作的理想工具。最初这包括采用数据表,因为它们有用的布局特征——尽管它们有臃肿的标记和不恰当的语义。最近,我们一直在强制使用浮动和绝对定位来实现大多数复杂的页面布局,但同样,这些功能都不是为布局网页而设计的。两者都有严重的限制,其中大部分我们只是训练自己去忍受。
幸运的是,最近的 CSS 模块引入了新的内容模型,专门用于创建灵活和健壮的页面布局。在撰写本文时,这些模块都处于不同的就绪状态,有些还没有可互操作的跨浏览器实现。我们将在接下来的章节中详细讨论其中的一些,以及它们带来的一些更有用的技术,但这只是对它们所提供的功能的一个快速总结。
灵活的盒子布局
我们之前提到的 Flexible Box 布局模块,或者说 flexbox ,是 CSS 3 中引入的布局模型。Flexbox 允许您水平或垂直布局一个盒子的子项,并确定这些子项的大小、间距和分布。它还允许您更改元素在页面上呈现的顺序,而不管它们在 HTML 源代码中的位置。Flexbox 作为正常流模型(内嵌和块)的升级,在内容本身及其如何影响大小方面提供了精确控制和灵活性的平衡。
Flexbox 得到了广泛的应用,但是在旧版本的 Internet Explorer 中,这种支持明显缺失或不完整。好消息是,它的构造方式可以让你把它和其他方法结合起来,比如 floats,来创建非常健壮的布局。我们将在第六章中详细了解 flexbox。
网格布局
Grid layout 是第一个成熟的 CSS 高级布局工具,其目标是取代复杂的页面布局,这些页面布局过去是用浮动和定位元素创建的。它提供了布局与源代码顺序的完全分离,并从内容结构和单个模块的表示中抽象出网格系统的概念。其中 flexbox 是“微观”,网格布局是“宏观”,所以这两种方法很好的互补。
网格布局还没有得到广泛的支持,但是在本书编写的时候,浏览器制造商正在竞相实现它。我们将在第七章中了解这个强大的新模块。
多栏布局
多栏布局模块是允许内容流入不同栏的一种相当简单的方式;例如,创建一个类似报纸的布局,其中段落的文本排列成多个垂直的列。该模块允许您选择一组列数或一个首选宽度,并根据可用空间选择列数。您还可以控制列之间的间距,并对这些间距应用类似边框的视觉效果。由于多栏布局更多的是一种排版工具,而不是一般布局,我们将在第四章中讨论它。
地区
CSS 区域允许您指定内容如何在页面上的元素之间流动。一个元素充当内容的来源,但是该内容可以流入页面上其他位置的其他占位符元素,而不是正常的块流。这意味着布局不再受 HTML 源代码顺序的影响,布局表示也不再受内容结构的影响。
CSS 区域允许以前单独使用 CSS 不可能实现的布局,并可能在未来推动某些基于打印的布局模式的采用。然而,很少有浏览器制造商对 CSS 区域表现出任何喜爱,而且这种类型的布局在一段时间内可能还不够成熟。出于这个原因,我们在本书中将不再详细讨论区域。
摘要
在本章中,您学习了盒子模型以及填充、边距、宽度和高度如何影响盒子的尺寸。您还了解了边距折叠的概念以及它如何影响布局。向您介绍了 CSS 的各种格式模型,如正常流、绝对定位和浮动。您了解了内联框和块框之间的区别,如何在相对定位的祖先中绝对定位元素,以及清除是如何工作的。
既然你已经掌握了这些基本原则,让我们开始好好利用它们吧。在本书接下来的章节中,你将会了解到一些核心的 CSS 概念,你将会看到它们是如何被应用到各种实用的技术中的。所以,启动你最喜欢的编辑器,让我们开始编码吧。
四、网络排版
自从印刷机发明以来,排版一直是平面设计的基本部分,所以你会期望它在网页设计领域发挥核心作用。有些人甚至说网页设计 95%是排版。因此,令人惊讶的是,浏览器直到最近才允许我们完全接受网页上的排版和排版。这为我们提供了从数百年的印刷历史中学习的可能性,并创造出样式丰富的内容,阅读起来令人愉悦。
以前版本的 CSS 掌握不包含一个单独的网页排版章节,所以也许这给了你一些过去几年在这个领域的进步。在本章中,我们将涉及许多领域:
-
如何使用基本的 CSS 字体和文本属性应用实心排印规则
-
控制度量、多栏文本和断字
-
使用自定义 web 字体和高级字体功能
-
使用阴影和其他技巧的文本效果
CSS 中的基本排版
大多数设计师要做的第一件事就是添加基本的印刷样式。从 body 元素开始,向下发展到越来越具体的规则,我们为可读性、清晰性和音调设置了基础。作为本章的第一个例子,我们将这样做:选取一个示例页面并应用基本的排版处理。
图 4-1 显示了一个非常简单的 HTML 文档(关于月球的文本,转载自维基百科),显示在浏览器中,没有添加样式。它仍然呈现为可读文档的事实是由于浏览器中的默认样式表,其中设置了一些相对合理的印刷规则。
图 4-1。一个简单的 HTML 文档,还没有应用样式
我们的简单文档包含几个标题和一些文本段落(在适用的地方有一些行内元素来增强文本),位于 article 元素中:
<article>
<h1>The Moon</h1>
<p> The <strong>Moon</strong> (in Greek: σελήνη...</p>
...
<h2>Orbit</h2>
<p>The Moon is in synchronous…</p>
...
<h3>Gravitational pull & distance</h3>
<p>The Moon's gravitational...</p>
<h2>Lunar travels</h2>
<p>The Soviet Union's Luna programme...</p>
<p class="source">Text fetched from...</p>
</article>
虽然未样式化的文档是可读的,但它远非理想。我们的目标是创建一个相对较短的样式表,以帮助提高页面的可读性和美观性。在图 4-2 中,我们看到了我们想要的最终结果。
图 4-2。应用了新字体属性的文档
让我们仔细检查每一个规则,分解术语,为什么制定规则,以及基本排版属性背后的 CSS 机制是如何工作的。
文本颜色
文本颜色可能是我们为文档设置的最基本的东西之一,但是我们很容易忽略它的效果。默认情况下,浏览器将大多数文本呈现为黑色(当然,链接除外;那些是充满活力的蓝色),这与白色背景形成了非常高的对比度。足够的对比度对于可访问性至关重要,但也可能在另一个方向走得太远。事实上,屏幕的对比度非常高,以至于对于较长的文本来说,白底黑字会显得过于密集,影响可读性。
我们将标题设为默认的黑色,并将段落设置为非常暗的蓝灰色阴影。链接也仍然是蓝色的,但是我们会稍微调低一点活跃程度。
p {
color: #3b4348;
}
a {
color: #235ea7;
}
字体系列
font-family 属性允许您按优先顺序列出您想使用的字体:
body {
font-family: 'Georgia Pro', Georgia, Times, 'Times New Roman', serif;
}
h1, h2, h3, h4, h5, h6 {
font-family: Avenir Next, SegoeUI, arial, sans-serif;
}
body 元素(以及几乎所有其他元素,因为 font-family 是继承的)有一个字体堆栈,包括“Georgia Pro”,Georgia,Times,“Times New Roman”,serif。Georgia 是一种几乎普遍可用的衬线字体,其中较新的 Georgia Pro 变体安装在一些版本的 Windows 10 上。如果两个版本的 Georgia 都不可用,那么 Times 和 Times New Roman back 也存在于许多系统中。最后,我们回到通用系统 serif 字体。
对于标题,我们将 Avenir Next 列为我们的首选,这种字体有许多变化,预装在现代 Mac OS X 电脑和 iOS 设备上。如果这种字体不可用,浏览器会查找 Segoe UI,这是一种类似的多功能无衬线字体,存在于大多数版本的 Windows 电脑和 Windows Phone 设备上。如果浏览器找不到,它将尝试使用 Arial 字体(可在各种平台上使用),最后使用任何通用的 sans-serif 字体作为当前平台的默认字体。
图 4-3 显示了这些字体在 Mac OS X 上的 Safari 9 中的外观,与 Windows 10 上的 Microsoft Edge 进行了比较。
图 4-3。我们的页面在 Safari 9 上使用 Avenir Next 和乔治亚进行渲染(左)与在微软 Edge 上使用 Segoe UI 和乔治亚进行渲染(右)
注意
衬线是字形笔画末端的小角度形状,常见于许多经典字体。无衬线只是指没有衬线的字体。
这种回退机制是 font-family 属性的一个重要特性,因为不同的操作系统和移动设备并不都有相同的可用字体。字体的选择也比字体是否存在更复杂:如果首选字体缺少文本中使用的字形,如重音字符,浏览器也将退回到字体堆栈中查找这些单独的字符。
关于各种操作系统上可用的默认字体的一些研究可以帮助您为项目选择正确的堆栈。你可以在 http://cssfontstacks.com 找到一个好的起点。
列表末尾定义的 sans-serif 和 seriffont 族被称为通用族,作为一个总括选项。我们也可以选择草书、幻想和等宽字体。serif 和 sans-serif 通用族可能是最常用于文本的族。当选择预格式化文本(如代码示例)的字体时,monospace 会尝试选择所有字符都具有相同宽度的字体,使字符跨行对齐。fantasy 和草书属类稍微不常见一些,但它们分别对应于更精致的装饰字体或类似笔迹的字体。
注意
严格来说,您不需要将包含空格的字体系列名称放在引号中,但这样做是个好主意。该规范只要求在字体系列名称与通用系列名称相同的情况下使用引号,但是对于包含可能会在浏览器中出错的非标准符号的名称,也建议使用引号。如果不考虑其他因素,代码编辑器中的语法高亮器通常会更好地处理带空格的名字,如果它们被引用的话。
字体和字体之间的关系
关于字体、字体系列和字体的术语会变得非常混乱。字样(也称为字体族)是字母、数字和其他共享一种样式的字符的形状(也称为字形)的集合。对于每个字形,字体可以有几种不同的变体,包括粗体、正常体和轻体、斜体、不同的数字显示方式、将几个字符组合成一个字形的连字以及其他变体。
最初,字体(或字体面)是一种字体的特定变体的所有字形的集合,被铸造成金属片。这些收藏品随后被用于机械印刷机。在数字世界中,我们用这个词来表示保存字体表示的文件。假设的字体“CSS Mastery”可以只是一个字体文件,也可以由包含“CSS Mastery Regular”、“CSS Mastery Italic”、“CSS Mastery Light”等的几个字体文件组成。
字体大小和行高
几乎所有浏览器的默认字体大小都是 16 像素,除非用户改变了他们的偏好。我们保留了默认的字体大小,而是选择使用 em 单位来调整特定元素的大小:
h3 {
font-size: 1.314em; /* 21px */
}
用于 font-size 时,em 单位是继承了 font-size 的元素的比例因子。例如,对于我们的 h3 元素,大小是 1.314 * 16 = 21px。我们也可以将字体大小设置为 21px,但是 ems 更灵活一些。大多数浏览器允许用户缩放整个页面,即使是像素也能很好地工作。使用 ems,如果用户仅改变他们偏好中的默认字体大小,测量也可以缩放。
由于 em 单位基于继承的大小进行缩放,我们也可以通过调整父元素的大小来缩放页面的一部分的字体大小。另一方面——也是使用 ems 的棘手之处——是我们不希望仅仅因为它在标记中的位置就意外地扩展它。考虑以下假设的样式规则:
p {
font-size: 1.314em;
}
article {
font-size: 1.314em;
}
前面的规则意味着默认情况下,p 和 article 元素的字体大小都是 21px。但这也意味着,作为 article 元素的子元素的 p 元素的字体大小将为 1.314em × 1.314em,大约为 1.73em 或 28px。这可能不是设计中的意图,所以当使用相对长度时,您需要跟踪尺寸计算。
当谈到字体大小时,我们可以用百分比来代替 ems。设置 133.3%和用 1.333em 完全一样,用哪个是个人喜好问题。作为最后的灵活测量,我们可以使用 rem 单位。它是一个缩放因子,就像 em 一样,但是总是基于根元素 em 的大小进行缩放(因此得名 rem),这意味着在 html 元素上设置的字体大小。我们使用了 rem 单位来为所有标题获得一致的页边距上限值:
h1, h2, h3, h4, h5, h6 {
margin-top: 1.5rem; /* 24px */
}
当 ems 用于盒子模型尺寸时,它与继承的字体大小无关,而是与元素本身的计算字体大小有关。因此,这种测量对于所有标题级别都是不同的。为了获得一致(但灵活)的值,我们需要使用 rem,或者在 ems 中为每个标题级别单独计算边距。
rem 单元相对较新,可以在所有现代浏览器中工作。作为 Internet Explorer 8(以及更早版本)等旧浏览器的后备,我们可以利用 CSS 的容错能力,在基于 rem 的声明之前声明一个边距的像素度量:
h1, h2, h3, h4, h5, h6 {
**margin-top: 24px;** /* non-scalable fallback for old browsers */
margin-top: 1.5rem; /* 24px, will be ignored by old browsers */
}
警告
还有基于物理尺寸的绝对测量单位,如 mm、cm、in 和 pt,主要用于打印样式表。这些不应该用于屏幕样式。我们不会在这里讨论打印样式表,但是会在第八章中讨论如何针对不同的媒体类型。
带刻度的字体大小
在决定使用哪种字体时,并没有硬性规定选择哪种字体。最重要的是要确保文本足够大以便阅读,然后努力找到在当前上下文中有意义的大小。一些人喜欢观察它,而另一些人相信测量是基于数学关系的。
我们将三个标题的大小松散地建立在一个被称为“完美第四”的数学尺度上每个增加的标题级别都是其自身大小比前一个级别大四分之一,或者(以反比关系表示)是其下一个级别的 1.3333333 倍。然后将尺寸四舍五入以匹配最接近的像素尺寸,并截断到三位小数:
h1 {
font-size: 2.315em; /* 37px */
}
h2 {
font-size: 1.75em; /* 28px */
}
h3 {
font-size: 1.314em; /* 21px */
}
当开始设计工作时,像这样的规模可以是一个很大的帮助,即使你最终通过感觉设置最终测量。你可以在www.modularscale.com/
的模块化比例计算器中摆弄一堆不同的预设比例(见图 4-4 )。
图 4-4。模块化比例计算器允许您使用字体和数学大小比例的组合
行距、对齐和线盒的剖析
当我们为文本设置额外的尺寸时,我们将开始看到各种印刷概念之间的关系。因此,有必要更深入地了解 CSS 内联格式模型,以及更多的印刷术语——至少当它应用于西方书写系统时。图 4-5 展示了组成一行文本的各个部分,使用了我们例子中第一段的前两个单词。
图 4-5。内联格式模型的组成部分和技术术语
<p>The <strong>Moon</strong>…[etc]</p>
我们在第三章中看到了内联格式的高级视图。每行文本生成一个行框。这个框可以通过表示内联元素(比如本例中的<强>元素)或者它们之间的匿名内联框,进一步分成几个内联框。
文本绘制在内嵌框的中间,即所谓的内容区域。内容区域的高度是字体大小测量的定义——在图 4-5 中“月亮”一词的后面,我们看到一个 1em × 1em 的正方形,以及它与字形本身大小的关系。给 em 单元命名的传统印刷术语“em”源于大写字母“M”的大小,但正如我们所见,这不是 web 印刷中的正确定义。
小写字母如“x”的上边缘决定了所谓的 x 高度。不同字体之间的高度差异很大,这解释了为什么很难给出准确的字体大小的一般建议——你需要用实际的字体进行测试,看看感觉大小是多少。在我们这里使用的 Georgia 中,x-height 相当高,使得它看起来比其他相同字体尺寸的字体要大。
然后,实际的字形被放置成在内容区域内的某个地方垂直平衡出现,这样,默认情况下,每个行内框在靠近底部的一条公共线上对齐,这条公共线被称为基线。字形也不一定受内容区域的约束:例如,在某些字体中,小写的“g”可能会突出显示在内容区域的下面。
最后,行高定义了行框的总高度。这通常被称为行距,或者在排印术语中,行距(发音为“ledding”),这是由于在印刷机上用于分隔字符行的铅字排字机块。与 mechanical 类型不同,CSS 中的前导始终应用于行框的顶部和底部。
从总行高中减去字体大小,得到的尺寸分成两个相等的部分(称为半前导)。如果字体大小为 21px,行高为 30px,每条半行距将为 4.5px 高。
注意
如果一个行框包含不同行高的行框,行框的行高作为一个整体将至少与最高的行框一样高。
设置行高
当设置行高时,我们需要考虑什么对当前字体有意义。在我们的文章示例中,我们为 body 元素设置了基本字体系列 Georgia 和 1.5 的行高:
body {
font-family: Georgia, Times, 'Times New Roman', serif;
line-height: 1.5;
}
行高通常在 1.2 到 1.5 之间。当你调整这个值时,你需要找到这些线既不太拥挤也不太间隔和不连接的地方。一般来说,x 高度较大的文本可以容忍更大的行间距,就像我们在 Georgia 的文本集一样。文本的长度和字体大小也很重要:较短的较小文本通常可以处理更紧凑的行高值。
我们用一个无单位的 1.5 来设置行高,简单来说就是当前字体大小的 1.5 倍。正文的字体大小为 16px,默认行高为 24px。
可以使用像素、百分比或 ems 来设置行高,但请记住,body 的所有子体都将继承该值。一个可能的“陷阱”是,即使对于百分比和 em,继承的行高也是行高的计算的像素值,而对于无单位值则不是这样。通过省略单位,我们确保特定元素的行高作为乘数被继承,总是与其字体大小成比例。
竖向定线
除了行高之外,内联框还会受到垂直对齐属性的影响。默认值为 baseline,这意味着元素的基线将与父元素的基线对齐。在文章的最后,我们引用了在维基百科上查找的日期,其中序数“rd”后缀用一个 span 标记:
<time datetime="2016-02-23">the 23**<span class="ordinal">**rd**</span>** of February 2016.</time>
我们将使用 vertical-align 和 super 关键字为该文本设置一个上标对齐方式(以及一个稍小的字体大小):
.ordinal {
**vertical-align: super;**
font-size: smaller;
}
其他可能的关键字有 sub、top、bottom、text-top、text-bottom 和 middle。它们都或多或少地与内容区域和父行框有着复杂的关系。仅举一个例子,text-top 或 text-bottom 将内容区域的顶部或底部与父行框的内容区域对齐,这仅在内联框的字体大小或行高与父行框不同时才有效。就像我们说的:复杂。
也许更直观的方法是将元素基线的垂直对齐方式从父基线向上或向下移动一个设定的长度——以像素为单位,或者相对于字体大小的长度(例如 em 或%)。值得注意的是,不仅行高值会影响一段文本的最终行距。如果行框中有一个使用垂直对齐移动的项目,该元素将推出最终的行框高度。图 4-6 显示了当通过不同的垂直对齐值移动元素时,我们文章中的一行文本会发生什么。
图 4-6。可以与 vertical-align 一起使用的各种关键字和值。请注意线条框的顶部和底部是如何被最大值推出的,从而增加了该线条的整体行高
注意
与内联文本相比,内联块和图像对垂直对齐的反应略有不同,因为它们不一定有自己的单一基线。当我们在第六章中查看一些布局技巧时,我们将利用这一点。
字体粗细
接下来,我们使用 font-weight 属性设置标题的权重。一些字体有许多变化,如 Helvetica 新光,Helvetica 新光粗体,Helvetica 新光黑色,等等。我们没有声明字体变体的名称,而是使用关键字(普通、粗体、粗体和浅体)或数值。数值被写成偶数,从 100 开始,然后是 200、300、400 等等,直到 900。
normal 的默认值映射为 400,bold 为 700,这是大多数字体中最常见的粗细。关键字 bolder 和 light 的工作方式稍有不同,用于使文本比继承的值更重或更轻。
值 100–300 通常分别映射到名称中带有“细”或“细线”、“超轻”和“轻”的字体。相反,值 800 或 900 将映射到名称中包含“超粗体”、“粗体”或“黑色”字样的字体。介于 500(中等)和 600(半粗体或半粗体)之间。
作为标题的默认设置,我们将中等粗细设置为 500,对于超粗体 h1 元素和半粗体 h2 元素有所不同:
h1, h2, h3, h4, h5, h6 {
**font-weight: 500;**
}
h1 {
**font-weight: 800;**
}
h2 {
**font-weight: 600;**
}
Avenir Next 和 Segoe UI(我们最喜欢的字体)都包含很多粗细变化。如果字体缺少所需的粗细,它可能会尝试模仿更粗的粗细,但不会比正常粗细。遗憾的是,人工加粗字体的效果往往不太理想。
字体样式
设置声明字体样式:italic 从字样中选择斜体样式,如果有字样的话。如果没有,浏览器会试图通过倾斜字体来伪造——同样,结果往往不太理想。斜体通常用于强调或区分不同语调的事物。在我们的例子中,我们用标签包装了月球的拉丁和希腊名字。这个标记最初是早期 HTML 实现的表示标记的残余,但在 HTML5 中被重新定义,用于标记传统的斜体文本,如名称。
<p>The <strong>Moon</strong> (in Greek: σελήνη **<i lang="el">Selene</i>**, in Latin: **<I lang="la">Luna</i>**)
虽然标签不表示斜体,但是浏览器默认样式表将字体样式设置为斜体:
i {
**font-style: italic;**
}
如果我们愿意,我们可以重新定义这个元素,使其显示为粗体、非字母化的文本:
i {
font-weight: 700;
**font-style: normal;**
}
除了斜体和默认的正常值,您还可以使用倾斜关键字(倾斜文本的另一种变体),但这很少使用,因为很少有字体附带倾斜样式。
转换大小写变体
有时设计要求文本以不同于 HTML 源代码的形式显示。CSS 允许您通过文本转换属性对此进行一些控制。在我们的示例中,h1 在标记中被写成大写(首字母大写),但是通过 CSS 被强制显示为大写(参见图 4-7 ):
图 4-7。我们的 h1 显示为大写
h1 {
**text-transform: uppercase;**
}
除了大写值之外,您还可以指定 lowercase 使所有字母小写,大写使每个单词的第一个字母大写,或 none 将大小写恢复为 HTML 中创作的默认大小写。
使用字体变体
CSS 还有一个名为 font-variant 的属性,它允许你为你的字体选择所谓的小型大写字母。Small-caps 是字体的一种变体,其中小写字母显示为大写(或大写)字母的形状已经“缩小”到它们的大小。适当的小型大写字母变体对字母形状有更大的尊重,而不仅仅是简单地缩小它们,但这些变体大多出现在更独特的字体系列中。如果没有这样的字体,浏览器会试图为你伪造。我们可以在我们的文档中包含缩写“NASA”的缩写标签上说明这一点(见图 4-8 ):
图 4-8。使用字体变量关键字 small-caps 使浏览器将大写字形向下收缩到 x 高度
<abbr title="National Aeronautics and Space Administration">NASA</abbr>
我们将把它与 text-transform: lowercase 规则一起应用,因为 HTML 源代码中的字母已经是大写的了。最后一个调整是将 abbr 元素设置为稍微小一点的行高,因为在某些浏览器中,小型大写字母似乎会将内容框向下推,从而影响整个行框的高度。
abbr {
text-transform: lowercase;
font-variant: small-caps;
line-height: 1.25;
}
CSS 2.1 规范将 small-caps 定义为 font-variant 属性的唯一有效值。在 CSS 字体模块级别 3 规范中,这一点已经扩展到包括大量表示选择替代字形的方式的值。浏览器在采用这些方面进展缓慢,但幸运的是有更好的支持方式来实现这一点;我们将在下一节高级排版技术中讨论它们。
改变字母和单词之间的间距
改变单词和单个字符之间的间距通常最好留给字体的设计者。CSS 确实允许你用一些粗糙的工具来改变这一点。
word-spacing 属性很少使用,但正如您可能猜到的那样,它会影响单词之间的间距。你给它的值指定了从默认的间距增加或减少多少,由当前字体中的空白字符宽度决定。以下规则将将0.1 毫米添加到段落中单词之间的默认间距:
p {
word-spacing: 0.1em;
}
同样,您可以使用 letter-spacing 属性影响每个字母之间的间距。对于小写文本,这通常不是一个好主意——大多数字体都是为了让你在阅读时一次识别整个单词的形状而设计的,所以打乱间距会使文本难以阅读。大写(或小型大写)字形更适合单独解释,就像首字母缩写词的情况一样。一点额外的间距实际上可以使它们更容易阅读。让我们通过给我们的缩写标签增加一点字母间距来尝试一下(见图 4-9 ):
图 4-9。应用于 abbr 元素的少量字母间距
abbr {
text-transform: lowercase;
font-variant: small-caps;
**letter-spacing: 0.1em;**
}
这是我们最后的字体相关设置和小的排版调整。接下来,我们将关注文本如何布局,以进一步确保良好的阅读体验。
小节、节奏和节奏
我们下一个关注的领域是使文本读起来愉快的一个关键因素:行的长度。在印刷术语中,这被称为度量。过长或过短的线条会扰乱读者在文本中的眼球运动,会导致读者迷失方向,甚至完全放弃文本。
对于什么是完美的线长度,没有确切的答案。这取决于字体的大小、屏幕的大小以及正在显示的文本内容的类型。我们所能做的是参考关于行长度一般规则的研究和历史建议,并尝试将它们合理地应用到我们的页面中。
Robert Bringhurst 的经典著作The Elements of Typographic Style指出,正文通常设置在 45 到 75 个字符之间,平均约为 66 个字符。在将这个建议应用到网络上时,排版专家 Richard Rutter 发现这个范围在网络上也很有效——至少在大屏幕上是这样。在非常小的屏幕(或在远处观看的大屏幕,如电视或投影)的情况下,尺寸与到屏幕的距离相结合可能保证短至 40 个字符的度量。
注意
我们将在第八章回到响应式网页设计的排版挑战。
可以通过在包围文本的元素上或者在标题、段落等上设置宽度来实现对行长度的约束。他们自己。
在我们的正文中,Georgia 字体有相对较宽的字母形式,这是 x 高度较大的结果。这意味着我们可以在更高的范围内进行测量。我们选择了简单的选项,将 article 元素的最大宽度设置为 36em(一个字符平均为 0.5em),使其在页面上居中。如果视窗比这个更窄,元素会自动缩小。
article {
max-width: 36em;
margin: 0 auto;
}
这使得我们的段落文本在更宽的视窗中的行长度约为 77 个字符,如图 4-10 所示。我们选择使用 ems 来应用宽度,这样即使我们或者用户决定改变字体大小,也可以很好地缩放尺寸。
图 4-10。article 元素的最大宽度被限制为 36em,即使我们增加了字体大小
文本缩进和对齐
默认情况下,我们的文本将被设置为左对齐。文本的左边缘保持平直有助于眼睛找到下一行,保持阅读速度。对于一段接一段的段落,通常要么在一行之间使用空白,要么少量缩进文本,以强调从一个段落到下一个段落的转换。在设置文章文本时,我们选择了后者,使用相邻的兄弟组合符和 text-indent 属性:
p + p {
**text-indent: 1.25em;**
}
文本的右边缘非常不均匀(见前面的图 4-9 ),我们暂时让它保持原样。这种不均匀的形状在印刷术语中被称为“rag”(如“ragged”)。末端边缘参差不齐并不是一种灾难,但是在对除了非常短的文本之外的任何内容使用居中对齐之前,您应该仔细考虑。居中文本最适合小块用户界面文本(如按钮)或短标题,但两边参差不齐会破坏可读性。
然而,我们已经将样本页面的 h1 居中。我们还给它加了一个底部边框,让它在视觉上锚定到下面的文章文本,如图 4-11 所示。
图 4-11。我们已经将 h1 居中对齐
h1 {
**text-align: center;**
border-bottom: 1px solid #c8bc9d;
}
text-align 属性可以接受几个关键字值,包括 left、right、center 和 justify。CSS 文本级别 3 规范定义了一些额外的值,包括开始和结束。这两个逻辑方向关键字对应于文本的书写模式:大多数西方语言是从左向右书写的,因此如果文档语言是英语,则起始值将表示左对齐,结束值将表示右对齐。在从右向左的语言中,如阿拉伯语,这将是相反的。如果您在父元素上设置了 dir="rtl "属性,大多数浏览器也会自动反转默认的文本方向,以指示从右向左的文本。
text-align 属性也可以使用值 justify,分布单词之间的间距,以便文本与左右边缘对齐,消除不规则的右边。这是印刷媒体中的一种常见技术,在这种情况下,可以调整副本、断字和字体属性,以匹配页面上的空间。
网络是一种不同的媒介,它的准确呈现取决于我们无法控制的因素。不同的屏幕尺寸、不同的字体安装和不同的浏览器引擎都会影响用户查看我们页面的方式。如果你使用对齐的文本,它可能会看起来很糟糕,变得很难阅读,如图 4-12 所示。空白的“河流”可能会在你的文本中形成,尤其是当小节减少的时候。
图 4-12。文本对齐的文本段落:对齐会导致单词之间出现“河流”
浏览器用来对齐文本的默认方法是一种相当笨拙的算法,其结果不如桌面出版软件中的精确。使用的算法类型可以通过 text-justify 属性进行更改,但是对各种值的支持很少,并且主要涉及如何调整除大多数西方书写系统之外的其他语言类型的字母形式和单词。
有趣的是,Internet Explorer 支持该属性的非标准值报纸,它似乎使用了一种更聪明的算法,在字母和单词之间分配空白。
用连字符号连接
如果你仍然想让页面中的文字对齐,断字可以在一定程度上帮助消除河流。您可以使用 HTML 实体在标记中手动插入软连字符。该连字符只有在浏览器需要断开它以适合该行时才可见(见图 4-13 ):
图 4-13。用软连字符手动断字
<p>The <strong>Moon</strong> […] is Earth's only natural satel**­**lite.[…]
对于像一篇文章这样的较长文本,你不太可能仔细检查并手动断字。使用连字符属性,我们可以让浏览器来完成这项工作。它仍然是一个相对较新的特性,所以大多数支持它的浏览器都需要厂商前缀。Internet Explorer 版之前的版本,Android 设备上的股票 WebKit 浏览器,以及令人惊讶的是,Chrome 和 Opera(在编写本文时)等基于 Blink 的浏览器根本不支持断字。
如果你想激活自动断字,你需要两段代码。首先,您需要确保设置了文档的语言代码,通常是在 html 元素上:
<html **lang="en"**>
接下来,将相关元素的连字符属性设置为 auto。图 4-14 显示了在 Firefox 中出现的结果。
图 4-14。激活自动断字会在 Firefox 中显示更直的右边横条
p {
hyphens: auto;
}
要关闭断字功能,可以将连字符属性设置为手动值。手动模式仍然尊重软连字符。
在多列中设置文本
虽然对文章整体宽度的 36em 限制有助于限制尺寸,但它确实会在较大的屏幕上浪费很多空间。这么多未使用的空白!有时,为了更有效地使用更宽的屏幕,同时保持合理的度量,将文本设置在多列中是有意义的。CSS 多列布局模块的属性为我们提供了实现这一点的工具,将内容分成相等的列。
“多列布局”这个名称可能会有点误导,因为这组属性并不是指为一个页面的不同部分创建具有列和间距的通用布局网格,而是指页面的一部分内容像报纸一样以列的形式流动。尝试将它用于其他目的肯定是可能的,但也许不可取。
如果我们将最大宽度增加到 70em,我们可以放入三列。我们可以通过将 columns 属性设置为所需的最小列宽来告诉文章自动将内容排列成列(参见图 4-15 )。列间距由列间距属性控制:
图 4-15。文章内容现在会自动流入 70em 最大宽度范围内的尽可能多的列中,只要它们的最小宽度为 20em
article {
**max-width: 70em;**
**columns: 20em;**
**column-gap: 1.5em;**
margin: 0 auto;
}
columns 属性是设置列数和列宽属性的简写。如果只设置列数,浏览器将生成一定数量的列,而不考虑宽度。如果设置列宽和计数,列宽作为最小值,而计数作为最大列数。
columns: 20em; /* automatic number of columns as long as they are at least 20em */
column-width: 20em; /* same as above */
columns: 3; /* creates 3 columns, with automatic width */
column-count: 3; /* same as above */
columns: 3 20em; /* at most 3 columns, at least 20em wide each */
/* the following two combined are the same as the above shorthand: */
column-count: 3;
column-width: 20em;
后退宽度
为了避免在不支持多列属性的浏览器中出现过长的行,我们可以添加一些规则来设置段落本身的最大宽度。旧的浏览器将会显示一个单独的列,但是仍然容易阅读:
article > p {
max-width: 36em;
}
列跨度
在前面的示例中,文章包装中的所有元素都流入列中。我们可以选择从那个流中剔除一些元素,迫使它们跨越所有列。在图 4-16 中,文章标题和最后一段(包含源链接)跨越所有列:
图 4-16。第一个标题和最后一个段落跨越所有列
.h1,
.source {
column-span: all; /* or column-span: none; to explicitly turn off. */
}
如果我们选择让流中间的元素跨越所有列,文本将被分成几个垂直堆叠的基于列的流。在图 4-17 中,h2 元素被添加到先前的规则中,显示了标题前后的文本如何在它自己的一组列中流动。
图 4-17。具有柱跨的元素:all 会将柱流分成几个垂直堆叠的柱集
几乎所有浏览器都支持多栏布局属性,IE9 和更早版本除外。尽管如此,一些警告仍然适用:
-
几乎每个浏览器都需要适当的供应商前缀来应用列属性。
-
在编写本文时,Firefox 根本不支持列跨度规则。
-
各种浏览器之间存在相当多的错误和不一致。大多数情况下,当元素跨列流动时,像边距折叠和边框呈现这样的事情会奇怪地发生。Zoe Mickley Gillenwater 有一篇关于这个和其他陷阱的文章:。
垂直节奏和基线网格
我们已经提到了排版中尺寸之间的一些数学关系是如何帮助它走到一起的。例如,我们使用“完美的第四”尺寸作为标题尺寸的基础。我们还为所有标题设置了一个通用的页边距-上限值 1.5 雷姆,等于一行正文的高度,并对各列之间的间距再次使用了相同的度量。一些设计师坚信这些和谐的尺寸,让基线高度作为设计其余部分的节拍器。
在印刷设计中,紧密遵循这种节奏是很常见的,这样正文的行就会落在一个基线网格上,即使标题、引用或其他部分不时打破这种节奏。它不仅有助于扫描页面时的眼球运动,还有助于防止(薄)纸另一面上的打印线条在双面打印中穿透,因为相同的基线网格适用于两者。
在 Web 上,获得正确的基线网格要挑剔得多——尤其是在处理可变大小和用户生成的内容(如图像)时。至少在某些情况下尝试是有意义的,比如多栏文本。在图 4-18 中,我们可以看到,由于标题的原因,各列的基线并没有完全对齐。
图 4-18。我们的多栏布局,叠加了基线网格。有些部分不合节奏
让我们调整标题的边距,使两个标题级别的上边距、行高和下边距之和等于基线行高值的倍数。这样,基线应该在所有三列中对齐。
h2 {
font-size: 1.75em; /* 28px */
line-height: 1.25; /* 28*1.25 = 35px */
margin-top: 1.036em; /* 29px */
margin-bottom: .2859em; /* 8px */
}
h3 {
font-size: 1.314em; /* 21px */
line-height: 1.29; /* 1.29*21 = 27px */
margin-top: .619em; /* 13px */
margin-bottom: .38em;/* 8px */
}
最初,所有标题的行高值都是 1.25,但是为了简化计算,我们已经覆盖了这个值。总的来说,上边距和下边距的划分多少是通过感觉来完成的。重要的是,这两条规则的总和是基线高度的倍数:h2 为 72px,h3 为 48px。所有三列中正文文本的基线现在应该对齐了(参见图 4-19 )。
图 4-19。多栏文章,现在设置了垂直节奏,以便所有段落都落在基线网格上
网页字体
到目前为止,在这一章中,我们已经把自己限制在本地安装在用户计算机上的字体。常见的网络字体如 Helvetica 字体、佐治亚字体和 Times New Roman 字体之所以常见,恰恰是因为它们传统上可以在流行的操作系统如 Windows 和 Mac OS X 上使用
多年来,设计者希望能够从网络上嵌入远程字体,就像他们可以将图像嵌入网页一样。自 1997 年 Internet Explorer 4 发布以来,这种技术就已经存在,但直到 2009 年 Firefox、Safari 和 Opera 引入类似技术后,才出现了良好的跨浏览器支持。
从那以后,网络字体被大量采用。最初在小型博客和个人网站上进行试验,随后大公司甚至政府组织(例如,见图 4-20 )都采用了定制的网络字体。
图 4-20。http://www.gov.uk 网站使用了由 Margaret Calvert 和 Henrik Kubel 设计的自定义字体
批准
处理网络字体的另一个复杂因素是许可。最初,字体代工厂对于允许个人浏览器下载他们的字体非常谨慎。人们担心这会导致对他们字体的不可控制的盗版,这种担心需要几年时间才能消除。
大多数在网上提供字体的铸造厂都要求对如何提供字体有一定的安全限制。例如,他们可能只允许从具有特定域名的站点下载链接到的字体,或者要求服务器上的字体名称定期更改以避免字体的热链接。
网络字体托管服务
如果您还没有开始尝试自定义字体,最简单的方法就是使用 web 字体服务。像 Adobe type kit(typekit.com
)、cloud . typography(【http://www.typography.com/cloud】)和 Fonts.com()这样的商业服务负责托管和提供网络字体的所有细节。还有谷歌字体(www.google.com/fonts
),谷歌从一系列字体代工厂收集并托管免费使用的字体。
这些在线服务处理与铸造厂的不同许可交易,以及将字体转换为正确文件格式的困难工作,确保包含正确的字符集并进行良好的优化。然后,他们从可靠的高速服务器上托管和提供这些字体。
选择托管服务允许您单独许可字体以供一次性使用,或者作为字体资源库订阅的一部分。托管服务消除了处理网页字体的大量痛苦,让你可以专注于网页中字体的设计和使用。
@font-face 规则
嵌入式 web 字体的关键是@font-face 规则。此规则允许您指定供浏览器下载的字体在 web 服务器上的位置,然后允许您在样式表的其他地方引用该字体:
@font-face {
font-family: Vollkorn;
font-weight: bold;
src: url("fonts/vollkorn/Vollkorn-Bold.woff") format('woff');
}
h1, h2, h3, h4, h5, h6 {
font-family: Vollkorn, Georgia, serif;
font-weight: bold;
}
前面的@font-face 块中的代码声明,当样式表使用字体系列值 Vollkorn 和粗体字时,应用此规则,然后提供一个 URL 供浏览器下载包含粗体字的 Web 开放字体格式(WOFF)文件。
一旦声明了这个新的 Vollkorn 字体,就可以在样式表的普通 CSS 字体系列属性中使用它。在前面的例子中,我们选择对页面上的所有标题元素使用粗体 Vollkorn 字体。
字体文件格式
尽管现在所有主流浏览器对 web 字体的支持都非常好,但对一致字体文件格式的支持却不太好。字体格式的历史悠久而复杂,并且与微软、苹果和 Adobe 等公司的历史紧密相连。幸运的是,所有的浏览器制造商现在都支持标准化的 WOFF 格式,有些甚至支持新的更高效的 WOFF2。如果你需要支持 IE8 和更早的版本,Safari 的旧版本,或者旧的 Android 设备,你可能需要用额外的文件格式来补充你的代码,比如 SVG 字体,EOT 和 TTF。
小费
如果你有网络字体使用许可的字体,你可以使用在线工具创建这些额外的格式,如字体松鼠(fontsquirrel.com
)。
为了处理这种不一致的支持,@font-face 规则能够接受 src 描述符的多个值(很像 font-family 的工作方式)以及 format()提示,让浏览器来决定哪个文件最适合下载。
使用这个特性,我们可以通过@font-face 规则获得对 web 字体的几乎通用的跨浏览器支持,如下所示:
@font-face {
font-family: Vollkorn;
src: url('fonts/Vollkorn-Regular.eot#?ie') format('embedded-opentype'),
url('fonts/Vollkorn-Regular.woff2') format('woff2'),
url('fonts/Vollkorn-Regular.woff') format('woff'),
url('fonts/Vollkorn-Regular.ttf') format('truetype'),
url('fonts/Vollkorn-Regular.svg') format('svg');
}
这涵盖了所有支持 EOT、WOFF(包括 WOFF2)、TTF 和 SVG 的浏览器,也就是说几乎现在使用的所有浏览器。它甚至解释了 IE6–8 中的古怪行为,通过声明带有 querystring 参数的第一个 src 值。这种模式被称为“Fontspring @font-face 语法”,在www . font spring . com/blog/further-hardening-of-bullet-syntax
中有详细的记录,以及它所考虑的格式和边缘情况。
注意
在 IE6–8 中使用 web 字体时,尤其是在使用同一字样的几种变体时,还会遇到一些问题。这里我们就不细说了,但是你可以从 Typekit 的这篇文章中找到更多的背景:blog . type kit . com/2011/06/27/new-from-type kit-variation-specific-font-family-names-in-ie-6-8/
。我们还在随书附带的代码示例中记录了变通方法。
在其余使用 web 字体的示例中,我们将只使用 WOFF 和 WOFF2 格式——通过使用这些格式,我们可以支持大多数浏览器,同时保持代码简单。
字体描述符
@font-face 规则接受许多声明,其中大部分是可选的。最常用的有
-
font-family:必选,字体系列的名称。
-
src:必选,URL 或 URL 列表,可以在其中获得字体。
-
字体粗细:可选的字体粗细。默认为正常。
-
字体样式:可选的字体样式。默认为正常。
重要的是要明白,这些不是你应用于常规规则集的相同字体属性——它们实际上根本不是常规属性,而是字体描述符。我们并没有改变任何关于字体的东西,而是解释了当在样式表中使用时,这些属性的哪些值应该触发这个特定字体文件的使用。
如果字体粗细在这里设置为粗体,这意味着“当这个字体系列中的某个设置的字体粗细设置为粗体时,使用这个块中的文件。”一个缺陷是,如果这是 Vollkorn 唯一可用的实例,它也将用于其他权重,尽管不是正确的权重。这是浏览器如何加载和选择字体的规范的一部分:正确的字体系列高于正确的权重。
许多 typefaces 有不同的字体以适应不同的粗细、样式和变体,所以您可能有几个不同的@font-face 块引用指向不同文件的字体族名称 Vollkorn。在下面的例子中,我们加载了两种不同的字体,并声明了每种字体的粗细和样式:
@font-face {
font-family: AlegreyaSans;
src: url('fonts/alegreya/AlegreyaSans-Regular.woff2') format('woff2'),
url('fonts/alegreya/AlegreyaSans-Regular.woff') format('woff');
/* font-weight and font-style both default to "normal". */
}
@font-face {
font-family: Vollkorn;
src: url('fonts/vollkorn/Vollkorn-Medium.woff') format('woff'),
url('fonts/vollkorn/Vollkorn-Medium.woff') format('woff');
font-weight: 500;
}
@font-face {
font-family: Vollkorn;
font-weight: bold;
src: url('fonts/vollkorn/Vollkorn-Bold.woff') format('woff'),
url('fonts/vollkorn/Vollkorn-Bold.woff') format('woff');
}
然后,我们可以在样式表的其他地方使用正确的字体文件,方法是声明我们需要的变体:
body {
font-family: AlegreyaSans, Helvetica, arial, sans-serif;
}
h1, h2, h3, h4, h5, h6 {
font-family: Vollkorn, Georgia, Times, 'Times New Roman', serif;
font-weight: bold; /* will use the Vollkorn Bold font. */
}
h3 {
font-weight: 500; /* will use the Vollkorn Medium font. */
}
将这些样式应用于我们在月球文章示例中使用的相同标记,我们得到了不同的外观,其中用于正文的 Alegreya sans-serif 字体系列与用于标题的 serif Vollkorn 形成了对比(见图 4-21 )。h1 和 h2 现在使用 Vollkorn Bold 字体文件,而 h3 自动使用 Vollkorn Medium,因为字体粗细匹配 500。
图 4-21。应用了新字体的文章示例
警告
在加载 web 字体时,一个常见的错误是在@font-face 块中加载粗体字体,并将其字体粗细描述符设置为 normal,然后将其用于 font-weight 属性设置为 bold 的元素。这导致一些浏览器认为这种字体没有合适的加粗变体,让他们在原来的加粗字体上加上“假加粗”。
在前面的例子中,我们可以看到字体系列的机制是如何与我们的新字体相结合的:原来 Alegreya Sans 字体不包含任何希腊字母,这些字母出现在月亮的翻译名称中(见图 4-22 )。对于这些字形,使用备用字体—在本例中为 Helvetica。从两种字体中不同的 x 高度可以明显看出这一点。
图 4-22。希腊字形使用字体系列堆栈中的备用字体。请注意 x 高度略有不同
坏消息是,我们没有为 Alegreya 加载斜体字体文件,对于缺失的字体样式,浏览器使用基于正常样式的“假斜体”。当我们查看文章最后的来源参考段落时,这一点变得更加清晰(见图 4-23 )。
图 4-23。我们文章底部的假斜体文本
幸运的是,Alegreya 包含了各种各样的变体,所以如果我们添加一个指向正确文件的新的@font-face 块,这个问题应该会为任何已经设置为 font-style: italic 的正文文本自行解决(见图 4-24 ):
图 4-24。现在用真正的斜体
@font-face {
font-family: AlegreyaSans;
src: url('fonts/alegreya/AlegreyaSans-Italic.woff2') format('woff2'),
url('fonts/alegreya/AlegreyaSans-Italic.woff') format('woff');
font-style: italic;
}
Web 字体、浏览器和性能
虽然网页字体为网页设计提供了一个相当大的飞跃,但它们的应用带有一定的免责声明。
很明显,下载额外的字体会增加用户的页面总重量。你首先要考虑的是限制你需要加载多少字体文件。同样非常重要的是,如果您托管自己的自定义字体,您必须应用适当的缓存头以最小化网络流量。但是,关于浏览器如何将字体实际呈现到屏幕上,还有其他一些考虑因素。
下载 web 字体时,浏览器有两种文本内容选择。首先,它可以阻止在屏幕上显示文本,直到网络字体已经下载并可供使用,这被称为不可见文本的闪光(或 FOIT)。这是 Safari、Chrome 和 Internet Explorer 默认显示的行为,它可能会导致用户无法阅读您站点的内容,因为字体下载速度很慢。对于在慢速网络连接上浏览的用户来说,这可能是一个特别的问题,如图 4-25 所示。
图 4-25。等待字体下载时,www.nike.com
上的一个页面
浏览器的另一个选择是在等待浏览器下载 web 字体时,以备用字体显示内容。这避免了缓慢的网络阻止内容的问题,但也有一个权衡,你得到了回退字体的闪光。这种闪现有时被称为无样式文本的闪现,或 FOUT。
这种无样式文本的闪烁会影响可感知的性能,尤其是当备用字体的规格与您试图加载的首选 web 字体不同时。如果在下载和应用字体时页面内容跳跃太多,用户可能会失去他们在页面中的位置。
如果您使用 web 字体,您可以选择通过 JavaScript 加载字体,以进一步控制使用哪种方法,以及如何显示 web 字体和回退字体。
用 JavaScript 加载字体
在最近的 CSS 字体加载规范中定义了一个用于加载字体的实验性 JavaScript API。遗憾的是,浏览器支持还不是特别广泛。相反,我们需要使用第三方库来确保一致的字体加载体验。
Typekit 维护一个开源的 JavaScript 工具,叫做 Web Font Loader(github.com/typekit/webfontloader
)。这是一个小的库,在后台使用本地字体加载 API,并在其他浏览器中模拟相同的功能。它支持一些常见的 web 字体提供商,如 Typekit、Google Fonts 和 Fonts.com,但也允许您自己托管字体。
你可以下载这个库,或者从谷歌自己的服务器上下载,详情见developers . Google . com/speed/libraries/# we B- font-loader
。
Web 字体加载器提供了许多有用的功能,但最有用的功能之一是确保字体加载的跨浏览器行为一致的能力。在这种情况下,我们希望确保缓慢加载的字体永远不会阻止用户阅读我们的内容。换句话说,我们希望在其他受支持的浏览器上启用 FOUT 行为。
Web 字体加载器为以下事件提供挂钩:
-
加载:字体开始加载时
-
活动:当字体完成加载时
-
无效:如果字体加载失败
在这个实例中,我们将把所有的@font-face 块移动到一个名为 alegreya-vollkorn.css 的单独样式表中,并把它放在一个名为 css 的子文件夹中。然后,我们将在示例页面的头部添加一小段 JavaScript 代码:
<script type="text/javascript">
WebFontConfig = {
custom: {
families: **['AlegreyaSans:n4,i4', 'Vollkorn:n6,n5,n7']**,
urls: ['css/alegreya-vollkorn.css']
}
};
(function() {
var wf = document.createElement('script');
wf.src = 'https://ajax.googleapis.com/ajax/libs/webfont/1/webfont.js';
wf.type = 'text/javascript';
wf.async = 'true';
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(wf, s);
})();
</script>
这段代码将下载 Web 字体加载器脚本本身,并配置我们使用的字体和变体(在代码中以粗体突出显示)。我们想要的变体在字体系列名称之后描述:n4 代表“正常样式,权重为 400”,等等。当在这个样式表中找到的字体被加载时,脚本会自动将生成的类名添加到 html 元素中。这样,您可以根据字体加载的当前状态来定制 CSS。
body {
font-family: Helvetica, arial, sans-serif;
}
.wf-alegreya-n4-active body {
font-family: Alegreya, Helvetica, arial, sans-serif;
}
这两个 CSS 规则意味着在 Alegreya 字体加载之前,我们将显示备用字体堆栈。然后,一旦 Alegreya 完成加载,加载器脚本将 wf-alegreya-n4-active 类添加到 html 元素,浏览器开始使用我们新下载的字体。现在,我们不仅可以看到跨浏览器的一致行为,还可以调整后备字体和网页字体的排版细节。
匹配后备字体大小
当字体正在加载但尚未完成时,应用类似的规则,我们可以避开 web 字体和 fallbacks 之间的字体度量差异。这一点很重要,因为当 web 字体替换后备字体时,您希望这种大小变化尽可能地谨慎和不引人注意。
在我们的例子中,Alegreya 字体的 x 高度明显小于 Helvetica 字体和 Arial 字体(两者具有相似的度量)。通过稍微调整字体大小和行高,我们可以非常接近地匹配高度。类似地,我们可以通过稍微调整单词间距来调整字符宽度的差异。这样,我们最终得到的结果更接近于网页字体加载后的文本外观。
.wf-alegreyasans-n4-loading p {
font-size: 0.905em;
word-spacing: -0.11em;
line-height: 1.72;
font-size-adjust:
}
小费
如果您使用的是垂直节奏,那么在使用这种技术时,您可能需要在几个地方调整这些类型的属性,以便不同的字体大小仍然与基本尺寸相对应。
我们将使用 web 字体加载器的另一个目的是在 Web 字体加载后设置字体大小调整属性。此属性允许您指定 x 高度和字体大小之间的纵横比。在首选字体中缺少字形的情况下,将调整备用字体的大小以匹配该比例。这通常会降低到大约一半高(值为 0.5),但它可能会有一点不同,使您的备用字体和您的首选 web 字体之间的差异非常明显。我们可以设置关键字 auto,让浏览器为我们完成这项工作,而不是手动测量并将这个值设置为一个数字:
.wf-alegreyasans-n4-active body {
font-size-adjust: auto;
}
在撰写本文时,Firefox 是唯一支持字体大小调整的浏览器,Chrome 在偏好标志后提供实验性支持。如果我们在 Firefox 中查看文章示例,如图 4-26 所示,我们可以看到希腊字形(在 Helvetica 看到的)现在与周围的 Alegreya 具有相同的高度。
图 4-26。Firefox 显示了调整了 x 高度的 Helvetica 希腊字形
高级排版功能
OpenType 是微软和 Adobe 在 20 世纪 90 年代开发的一种字体格式,允许在字体文件中包含字体的附加特征和功能。如果您使用的字体文件包含 OpenType 特性(可以包含在。ttf,。otf 或者。woff/.woff2 文件),您可以在大多数现代浏览器中控制一系列 CSS 功能。这些特征包括字距调整、连字和替代数字,以及装饰性特征,如图 4-27 所示。
图 4-27。“胖脸”字体上带有花式符号的“&”会议发言人的名字
CSS 字体规范有许多 OpenType 特性的目标属性,如字体字距调整、字体变体数字和字体变体连字。对这些目标属性的支持目前还不能跨浏览器使用,但有一些方法可以通过另一个更低级的属性(字体功能设置)来访问这些功能,该属性在许多现代浏览器中都有支持。通常,您最好两者都指定,因为有些浏览器可能支持目标属性,但不支持低级设置。
font-feature-settings 属性通过向其传递四个字母的 OpenType 代码(可选地带有一个数值)来接受切换某些功能集的值。例如,我们可以启用连字——将两个或更多字符组合成一个的字形——如图 4-28 所示。
图 4-28。Vollkorn 中的两个文本集,第一个没有连字,第二个启用了连字。请注意“fi”、“ff”和“fj”对的区别
字体设计者可以指定几种类型的连字,这取决于它们是用于一般情况还是特殊情况。要启用 Vollkorn 字体中的两种连字,即标准连字和 ?? 自选连字,我们将使用以下规则:
p {
font-variant-ligatures: common-ligatures discretionary-ligatures;
font-feature-settings: "liga", "dlig";
}
在支持 OpenType 的浏览器中,使用 font-variant-ligations 时,默认情况下总是启用标准连字,因此它们在第一个声明中被忽略。某些浏览器支持字体功能设置属性,但语法略有不同,而其他浏览器需要该属性的供应商前缀版本,因此打开常用和任意连字的完整规则是
h1, h2, h3 {
font-variant-ligatures: discretionary-ligatures;
-webkit-font-feature-settings: "liga", "dlig";
-moz-font-feature-settings: "liga", "dlig";
-moz-font-feature-settings: "liga=1, dlig=1";
font-feature-settings: "liga", "dlig";
}
语法差异需要解释一下:
-
影响 OpenType 特性的标准方式是在引号中使用它的四个字母的代码,后面可选地跟着一个关键字(开或关)或一个数字。这些代码指示功能的状态,如果您忽略它们(如前面的示例),它们将默认为 on。
-
使用 0 表示状态也会关闭该功能。如果该功能只有开和关两种状态,则值为 1 会将其打开。一些功能有几个“状态”,可以通过为每个状态使用适当的数字来选择这些状态,这意味着什么取决于您想要激活的单个字体和功能类型。
-
当几个特性同时受到影响时,它们需要用逗号隔开。
-
大多数浏览器仍然将这些特性作为厂商前缀来实现,所以要确保包含这些特性。
-
一些 Mozilla 浏览器的旧语法略有不同:所有受影响的功能都以逗号分隔的方式命名在一个带引号的字符串中,状态受等号和数字部分的影响。
OpenType 特性代码的完整列表可以在微软 http://www.microsoft.com/typography/otspec/featurelist.htm 的找到。在其余的例子中,我们将只使用字体特征设置的标准化形式以及目标特征属性。
数码
有些字体包含多种不同情况下使用的数字样式。许多字体,如 Georgia 或 Vollkorn,默认使用旧式数字,数字和字母一样有上升和下降。Vollkorn 还包括内衬数字,其中数字位于基线上,与大写字母具有相同的一般高度。在图 4-29 中,我们使用以下代码在旧式数字和衬里数字之间进行了明确切换:
图 4-29。衬里数字(上)与 Vollkorn 字体的旧式数字(下)
.lining-nums {
font-variant-numeric: lining-nums;
font-feature-settings: "lnum";
}
.old-style {
font-variant-numeric: oldstyle-nums;
font-feature-settings: "onum";
}
大多数字体都有不同宽度的数字(比例数字),就像普通的字母一样。如果您在表格或列表中使用的数字需要垂直对齐,您可能想要切换到表格数字。在图 4-30 中,我们将它们与衬里数字结合使用,配置如下:
图 4-30。Alegreya Sans 中设置的表格衬里数字。右边的价格垂直排列,尽管宽度不同
table {
font-variant-numeric: tabular-nums lining-nums;
font-feature-settings: "tnum", "lnum";
}
字距调整选项和文本呈现
高质量的字体内部通常有数据来调整某些字形对之间的间距。这个微调间距的过程被称为字距调整。这意味着一些字母对之间可能需要额外的空间,以使彼此看起来不太拥挤,一些甚至需要稍微重叠,以使彼此看起来不太远。在图 4-31 中可以看到一些常见字距对的例子,这里我们已经激活了 Alegreya 字体的字距。
图 4-31。一个没有(上)和有(下)详细字距调整的句子。注意像“AT”、“Ad”和“Ta”这样的词对之间的空间是如何缩小的
浏览器中的文本呈现大多试图基于已知的度量自动处理这一点,但您也可以在许多现代浏览器中激活从单个字体读取详细的字距调整数据。我们通过设置字体字距微调属性或激活 kern OpenType 特性来触发它:
.kern {
font-kerning: normal;
font-feature-settings: "kern";
}
关键字 normal 告诉浏览器从字体中获取字距调整数据(如果有)。auto 值允许浏览器在适当的时候打开它;例如,对于非常小的文本大小,它可能会被忽略。最后,您可以通过将该值设置为 none 来显式关闭它。
注意
在某些浏览器中,激活其他 OpenType 功能(如连字)可能会自动触发使用字体的字距调整数据,因此如果您想要关闭字距调整但仍使用连字,则需要明确指定。相反,使用“字距”功能也可能会触发常用或标准连字的应用。
避免文本呈现属性
设置声明 text-rendering:optimize legability 是同时激活字距调整和常用连字的另一个技巧。它不是任何 CSS 标准的一部分,而是 SVG 规范中的一个属性,告诉浏览器选择一种在 SVG 中呈现字母形状的方法。它可以优先考虑性能(optimizeSpeed)、更精确的形状(optimizeGeometricPrecision)或更可读的形状(optimizeLegibility)。
这个属性已经存在了一段时间,并且得到了很好的支持,所以经常可以看到站点使用它——在基于 WebKit 的旧浏览器支持字体功能设置之前,这是激活这些功能的唯一方法。但是,您应该知道,使用该属性会产生很多严重的渲染错误,所以您最好避免使用它。
文本效果
当涉及到网页排版的基础知识时,仍然有很多需要探索的地方,有些情况下,你可能会对标题和标识之类的东西着迷。在这一节中,我们将看看一些让你超越自我的技巧的例子,这些技巧可以创造出引人注目的效果,使你的网站与众不同。
使用和滥用文本阴影
CSS 文本阴影属性允许你在一段文本后面画一个阴影。对于较长的正文来说,这通常不是一个好主意,因为这会降低文本的可读性。对于标题或其他短文本,它确实有一些很好的用途,特别是用于创建“凸版印刷”一样的效果或重新创建传统绘画标志的阴影。
文本阴影的语法非常简单。您需要提供 x 轴和 y 轴相对于原始文本的偏移长度(正或负)、模糊距离的长度(其中 0 表示完全清晰的阴影)和颜色,所有这些都用空格分隔(参见图 4-32 ):
图 4-32。一个简单的文本阴影 应用了一些扩散。除 0 之外的任何扩散值都意味着阴影模糊
h1 {
text-shadow: -.2em .4em .2em #ccc;
}
除此之外,您可以使用逗号分隔的阴影列表为一段文本创建多个阴影。应用多个阴影时,它们会堆叠在一起,定义的第一个阴影显示在顶部,其他阴影堆叠在它的后面,按照定义的顺序逐渐向下堆叠。
给一个文本添加多个阴影的能力使它成为一个多才多艺的效果。这可以让你模拟一种“凸版印刷”效果,通过添加一个较暗和一个较亮的阴影,在文本上方或下方突出来,字体看起来要么被压入页面,要么被凸起(见图 4-33 )。浅色和深色阴影的偏移量取决于文本是比背景更亮还是更暗:上面有浅色阴影、下面有深色阴影的深色文本通常会出现在页面中,反之亦然。
图 4-33。简单的“凸版印刷”效果
以下代码示例说明了两种不同的效果:
.impressed {
background-color: #6990e1;
color: #31446B;
text-shadow: 0 -1px 1px #b3d6f9, 0 1px 0 #243350;
}
.embossed {
background-color: #3c5486;
color: #92B1EF;
text-shadow: 0 -1px 0 #243350, 0 1px 0 #def2fe;
}
在多重阴影技术的基础上,我们可以创建看起来像伪 3D 阴影样式的字体,模仿手绘标牌的样式。添加大量清晰的阴影,其中每个阴影之间的对角线偏移为一个像素或更少,使我们能够实现这种效果:
h1 {
font-family: Nunito, "Arial Rounded MT Bold", "Helvetica Rounded", Arial, sans-serif;
color: #d0bb78;
text-transform: uppercase;
font-weight: 700;
text-shadow:
-1px 1px 0 #743132,
-2px 2px 0 #743132,
-3px 3px 0 #743132,
/* …and so on, 1px increments */
-22px 22px 0 #743132,
-23px 23px 0 #743132;
}
这给了我们在图 4-34 中看到的时髦的 70 年代样式。文本设置在 Nunito 中,从 Google 字体加载。
图 4-34。随着偏移量的增加,大量的文本阴影会从文本中创建一个对角线阴影
为了进一步增加手绘标牌的感觉,我们可以应用一些效果。首先,我们可以用第一批白色阴影创建一个轮廓效果,因为标志画家通常会在字母和阴影之间留一些空间,这样他们可以更快地工作,因为字母中的油漆不必干燥,就可以移动到阴影中。我们将需要复制白色的阴影,并在所有方向上使用偏移,使其完全围绕字母。
其次,我们可以使用另一个技巧,使阴影看起来随着它的方向改变颜色,创造一个更加伪 3D 的外观,模拟照明方向。这是通过以交错的方式偏移各个阴影来实现的,其中颜色在较亮和较暗的颜色之间交替。这样,我们就利用它们的叠加来使一种颜色在水平方向更加突出,而另一种颜色在垂直方向更加突出。最终结果如图 4-35 所示。
图 4-35。我们完成的阴影标题
下面是所描述的两个技巧的结果代码:
h1 {
/* some properties left out */
text-shadow:
/* first, some white outline shadows in all directions: */
-2px 2px 0 #fff,
0px -2px 0 #fff,
0px 3px 0 #fff,
3px 0px 0 #fff,
-3px 0px 0 #fff,
2px 2px 0 #fff,
2px -2px 0 #fff,
-2px -2px 0 #fff,
/* …then some alternating shades that increasingly stick out in either direction: */
-3px 3px 0 #743b34,
-4px 3px 0 #a8564d,
-4px 5px 0 #743b34,
-5px 4px 0 #a8564d,
-5px 6px 0 #743b34,
/* ..and so on… */
-22px 21px 0 #a8564d,
-22px 23px 0 #743b34,
-23px 22px 0 #a8564d,
-23px 24px 0 #743b34;
}
在 Typekit Practice 网站(practice.typekit.com/lesson/using-shades/
)上可以找到一篇关于这种网络阴影和旧式标牌技术的深入文章,该网站也有大量其他学习网络排版艺术的资源。
几乎所有的浏览器都支持文本阴影属性,只有 IE9 和更早的版本没有支持。至于性能,如果支持的话,绘制阴影可能是非常昂贵的操作,所以你应该在你的设计中尽量少用阴影效果。
使用 JavaScript 增强排版
在某些情况下,纯粹的 CSS 并不能解决问题。例如,您可以使用:first-letter 伪元素定位一段文本的第一个字母,但是没有选择器可以单独定位其余的字母。例如,如果您希望每个字母都有不同的颜色,那么您唯一的选择就是用一个元素(比如一个)将每个字母包装起来,并以这些元素为目标。这种方法不太可行,尤其是如果您无法手动控制想要设置样式的元素的标记。
幸运的是,我们可以将这些视觉效果视为一种增强,并使用 JavaScript 自动创建额外的挂钩。lettering.js jQuery 插件(letteringjs.com
)将会做到这一点。这个插件背后的一个人是设计者和开发者 Trent Walton。图 4-36 在他个人网站的一个标题中显示了在野外使用的 lettering.js。
图 4-36。一个使用 lettering.js jQuery 插件的例子,来自trentwalton.com
有大量不同的基于 JavaScript 的解决方案可以帮助你调整文本。以下是一些例子:
-
fitText.js:来自 lettering.js(来自 agency Paravel)背后的同一个人的 jQuery 插件,用于根据页面大小调整文本大小(
fittext.js
)。 -
BigText.js:来自 Filament Group 的 Zach Leatherman 的一个脚本,它试图根据其容器(
github.com/zachleat/BigText
)使一行文本尽可能大。 -
鳏夫:来自 Gridset.com 的内森·福特的一个脚本,它通过在距离段落结尾一定距离的单词之间插入不间断空格字符(
github.com/nathanford/widowtamer
)来确保防止意外的寡妇。
注意
SVG 实现了一些非常酷的文本效果,这通常超出了本书的范围。然而,在第十二章中,我们将会看到一些先进的视觉效果技术,其中包括使用 SVG 的可缩放文本的简要介绍。
进一步的类型灵感
网络上的字体设计是一个丰富的研究、实验和挑战极限的领域。有数百年的历史和传统可以探索,并研究我们可以应用什么以及我们如何在网络环境中明智地应用它。
罗伯特·布林赫斯特(Robert Bringhurst)所著的《排版样式的要素》(The Elements of Typographic Style)一书是排版方面的权威之一,该书记录并解释了这一传统的大部分内容。它谈到了我们在本章中讨论过的许多特性,比如垂直节奏,断字和单词间距的细微差别。
前面提到的 Richard Rutter 已经花时间思考如何将 Bringhurst 建立的一些最佳实践带到 Web 上。应用于 Web 的排版样式元素(【http://webtypography.net】)展示了如何使用 HTML 和 CSS 来应用排版传统的特性,如果你对如何为 Web 排版获得更详细的规则和实践感兴趣,这是非常值得一看的。
另一个很好的排版实践指南是 Buttericks 的实用排版,它解释了如何将建议翻译成 CSS,可以在 http://practicaltypography.com/的买到。
最后,杰克·吉尔特索夫的字体链接集“网络字体”(typographyontheweb.com
),是一个关于设计和代码的绝佳资源。
记住,如果你在网页上添加任何文本,那么你就是在排版。
摘要
在这一章中,我们已经学习了 CSS 中文本和字体属性的基础知识,以及如何使用它们来获得最大的可读性和灵活性的一些技巧。使用多列布局模块,我们以类似报纸的格式创建了文本集。我们看到了行高和其他间距属性中的系统距离如何让您将文字设置为垂直节奏。
我们研究了如何使用@font-face 规则加载自定义字体,以及影响加载哪个字体文件的各种参数。我们还快速了解了如何使用 Web 字体加载器 JavaScript 库来控制字体加载的感知性能。
我们看了一些更详细的 OpenType 选项,这些选项可用于增强印刷控制—连字、数字和字距—以及字体功能设置属性如何允许我们对如何打开或关闭这些功能进行低级控制。
最后,我们探索了一些尝试更激进的标题和海报字体排版技术的方法,使用文本阴影和 JavaScript 的进一步帮助。
在下一章,我们将看看如何为你漂亮的排版页面搭建舞台:使用图像、背景色、边框和阴影。