大家好,欢迎来到IT知识分享网。
原文:Architecting CSS
协议:CC BY-NC-SA 4.0
一、级联样式表
这本关于级联样式表(CSS)的书采用了与大多数书截然不同的方法。它不是试图教你如何设计网页,除了粗略的概述之外,也不是专注于教你如何使用 CSS。本章介绍了本书的重点,即如何(以及为什么)将 CSS 作为一种编程语言来对待。
分类
级联样式表(CSS)是一种允许将布局、主题和样式应用于文档的 web 技术。在大多数情况下,所讨论的文档是超文本标记语言(HTML)文件,并且由 web 浏览器执行呈现。
CSS 通常被视为一种设计工具,因为它允许网页的作者或设计者决定网页的视觉外观。因为它控制着网页的最终外观,CSS 对可用性和可访问性都有直接的影响。由于这些因素,创建样式表和编写 CSS 有时被认为是设计任务,而维护样式表的任务可能是软件团队中的设计师。
有趣的是,在 CSS 成为 Web 的主流样式语言之前,有许多其他的竞争提案。然而,
- “CSS 有一个与众不同的特点:它考虑到在网络上,文档的风格不能由作者或读者自己设计,而是他们的愿望必须以某种方式结合或级联;事实上,不仅仅是读者和作者的愿望,还有显示设备和浏览器的功能。” 1
那么,CSS 的核心就是把控制权交到作者和读者手中。这使得它具有一定的交互性,并服从于网页读者的意愿,因为他们能够根据自己的喜好影响页面的最终外观。最常见的情况是,当作者的意图与最终用户的影响相遇,以创建一个独特的混合输出,这就是所谓的编程。所以这就引出了一个问题:到底什么是 CSS?写样式表应该算编程吗,写 CSS 的应该算程序员吗?
对于初学者来说,就像 JavaScript 和 Python 等流行的编程语言一样,CSS 是一种语言。如“结构”一节所示,CSS 有一个必须遵循的特定语法,您编写的规则导致操作被执行。此外,万维网联盟(W3C)将 CSS 称为一种语言。 2
衡量一门编程语言的一个标准是看它是否是图灵完备的。跳过形式定义,图灵完全语言的简单解释是可以解决任何任意计算。请注意,这不是一个严格的要求,有一些非常有用的编程语言并不是图灵完整的,最著名的是结构化查询语言(SQL)和正则表达式(RegEx)。然而,如果一种语言可以被证明是图灵完备的,那么它将消除所有关于其分类的疑问。CSS + HTML 的组合已经得到了被归类为图灵完备的必要的形式证明。 3
这意味着 CSS + HTML 符合任何通用编程语言的要求,写 CSS 和 HTML 算编程。这意味着你绝对是一名程序员(或者 web 开发人员,如果你喜欢的话)。
语言特征
尽管 CSS 被归类为一种编程语言,我们可能会同意使用 CSS + HTML 来完成一般的编程任务并不特别方便。这是因为这真的不是 CSS(或 HTML)的重点。
不管怎样,这种语言有许多有趣的特性与更传统的编程语言相似,包括
- 变量
- 功能
- 计算
- 进口
- 范围
- 评论
- 多态性
当利用 CSS 预编译器时,您可以获得更多的编程语言特性,例如
- 混入类
- 延长
- 命名空间
- 列表和映射数据结构
- 数学表达式
参见第章第二部分更深入地探究 CSS 语言特性,参见第章第七部分更多地了解 CSS 预编译器。
结构
值得注意的是,CSS 是一种声明性语言,而不是命令性语言。这意味着,我们不是编写代码来告诉 web 浏览器如何将样式应用于页面,而是告诉浏览器应用什么样式以及在哪里应用它们。这些声明在规范中被称为规则集,但也可以简称为规则。
CSS 中的每个规则都由一个或多个选择器和一个或多个声明组成,如图 1-1 所示。
图 1-1
CSS 规则集
每个声明都由一个属性值对组成。在撰写本文时,CSS 工作组列出了 564 个可能的属性。每个属性都必须得到用户代理(通常是 web 浏览器)的支持才能生效。不支持的属性会被忽略。
规则集可以通过 at-rules 如@media
或@supports
进一步分组和修改,并被收集到样式表中。样式表只是一个带有. css 扩展名的文本文件,它包含许多描述文档或网页表示的规则。
软件体系结构
一旦我们承认 CSS 拥有编程语言的所有复杂性,我们就需要接受这样的暗示:我们必须像对待代码一样对待样式表。这意味着我们可以利用软件架构的许多原则、最佳实践和设计模式,并在编写 CSS 时应用它们。
Note
您可能会发现术语软件架构与术语软件设计可以互换使用。这在行业内很常见,这两个术语指的是相同的高级设计思想和过程方法。由于 CSS 经常用于视觉设计,我们在本书中选择了术语架构来避免这些概念之间的混淆。
软件架构着眼于系统的结构和组件,并权衡各种可能的组合和方法的利弊。各种系统和方法的优点、缺点和局限性都应该考虑。架构师的方法更像是高层次的鸟瞰图,而不是开发人员的(尽管通常是同一个人做这两项工作)。
例如,如果您想让一个图像在单击按钮时在页面上移动,您将如何实现该功能?你会使用 CSS 还是 JavaScript?你会使用<img>
元素、可缩放矢量图形(SVG)还是画布?哪个会产生最流畅的视觉动画?当需求改变时,哪种方法最容易维护?这些是软件架构试图评估的问题类型。
做这些决定时,你不必从头开始。有一些公认的软件架构原则和最佳实践,可以指导您在 CSS 方面做出更具战略性的决策。
关注点分离
术语关注点分离被认为是埃德格·迪克斯特拉 4 的功劳,指的是一次专注于问题的一个方面非常有帮助的想法。正如后面的“web 架构”一节所显示的,Web 应用程序将内容、风格和动作分开,甚至对每一个关注点使用不同的技术。
看看与 CSS 相关的关注点分离,我们可能会在规则集中发现哪些关注点?如图 1-2 所示,我们看到布局、主题、版式和交互都是可以用 CSS 控制的网页的各个方面。见图 1-2 。
图 1-2
CSS 关注的领域
现在,假设您有一个包含 20,000 个规则集的样式表。这显然是难以管理的,这些规则集需要被分割到多个文件中。如何确定需要多少个文件以及每个文件包含哪些规则集?一种方法是基于关注点(例如,布局与主题)分割文件,而另一种方法是基于规则集所应用的特定组件对规则集进行分组。这个问题对于第十章中不同 CSS 架构模型的讨论来说非常重要。
软件架构的两个最广泛接受的原则,内聚和耦合,用来更好地定义关注点分离的概念。这些度量标准首次发表在结构化设计 5 中,并从此成为软件工程中的标准。
内聚力
凝聚力可以被描述为一种责任的度量。它是对给定代码单元所负责的不同任务或效果的广度以及这些任务或效果之间的关系性质的定性度量。传统上,衔接有七个等级,从巧合(最差)到功能(最好)。
另一个与凝聚力相关的流行原则是单一责任原则 (SRP)。这个想法是每个功能和模块应该只有一个职责。由此衍生出两个重要目标:
- 缺少副作用
–
如果一个函数只做一件事,那么它的使用几乎没有副作用或意想不到的后果的风险。 - 改变的理由只有一个
–
每次代码改变,都会增加引入错误和 bug 的风险。如果我们减少变更的数量,我们就能减少风险。此外,这有助于避免系统范围变化的副作用。
内聚和单一责任的目标都是促进简单性和减少风险,这是我们所有架构决策的重要目标。
耦合
耦合描述了两个或多个代码单元之间的相互依赖。松散耦合与良好的内聚性相关联,通常描述具有良好可重用性的模块,该模块可以独立于其他模块进行更新,对整个系统的影响最小。这是健壮和灵活系统的一个重要属性。
紧密耦合与较差的内聚性相关联,并且描述了难以独立测试或修改的模块。这种模块通常不能自由重用,并且在改变时可能需要更大的测试工作量。尽可能支持松散耦合。
当构建 web 应用程序时,我们会发现减少内容和设计之间的耦合有很多价值。理想情况下,我们应该能够创建适用于各种内容的样式表,而无需调整。当我们做到这一点时,我们可以说我们有正交性。
正交性
虽然在讨论系统设计时,正交性是一个重要且常见的术语,但近年来,这个词已经积累了一些不利因素。这可能是由于误用和不良定义的结合,导致它有时被描述为“技术术语” 6 然而,正交性是一个重要的概念,它与 7 的内聚和耦合直接相关,并且它将成为我们在本书后面讨论的许多决策的因素。
正交性描述的是一种合作而非相互依赖的关系,其中两件事情朝着共同的目标一起工作,同时保持一定程度的独立性。
在数学中,两个向量的正交性的最简单形式是当它们彼此垂直时,这意味着它们形成直角并且只相交一次。正交性也可以被描述为统计独立性,意味着两个(或更多)因素的变化不会相互影响
在计算机软件中,我们使用正交性来描述两个模块或组件之间的关系,这两个模块或组件能够彼此独立地改变。例如,如果我们能够编辑一个 HTML 文件来更改页面的内容和/或结构,而不对 CSS 进行相应的更改,则可以认为 HTML 页面与其 CSS 正交,但页面的视觉设计在更改后仍不受影响。
事实上,文档布局和结构之间的这种关注点分离是 CSS 背后最初的设计考虑之一。
非技术因素
为了实现关注点的分离,我们必须首先练习将复杂且具有挑战性的问题分解成简单部分的艺术。我们经常发现,看似不可能的任务其实是许多简单任务的大量积累。在学习看到单个部分的过程中,我们现在有了创建解决方案所需的构件。
除了软件架构的技术方面,我们的决策中还必须考虑一些实际的因素。
维护成本
人们很容易接受这样的观点,即今天最便宜的东西就是最好的财务决策;然而,软件产品的真实拥有成本必须包括计算中的持续维护成本。通常,建造成本最低的东西可能维护成本最高。也许我们可以购买现有的第三方库或模板,并从它们那里获取更新,而不是自己构建和维护?
程序调试时间
我们经常在紧迫的期限内工作,不断努力为客户创造价值,为公司创造收入。架构决策的总时间和工作量是一个重要的决策点,因为它可能会影响成本和时间表。有时候值得一开始就尝试一种新方法,随着时间的推移,这种方法会变得更快。其他时候,我们需要承认使用我们已经熟悉的东西是最好的选择。但是一定要考虑到开发时间是非常昂贵的,所以有时一个看起来微不足道的决定(在开发期间减少 10 秒的页面重新加载时间)可能会在以后产生回报(10 秒 x 每天 100 次 x 260 个工作日 x 5 个开发人员=每年节省 15 天)。
开发者满意度
虽然我们决策的技术和财务影响相对直接,但决策对士气的影响同样重要,而且容易被忽略。因此,当在 CSS、Sass 和 Less 之间做出决定或者选择下一个 CSS 框架时,团队的态度和认同是一个重要的考虑因素。有时,摩擦可能是通常对变革的阻力或变革的速度;有时这是一个合理的担忧,即决策不是最适合产品或团队的。然而其他时候,这是因为开发人员没有感觉到决策在帮助他们建立有用的技能。认真对待这些问题,因为士气会影响绩效、生活质量和人员流动。
最佳实践
重要的是要认识到,对架构的研究围绕着为常见问题定义解决方案模式,但也没有绝对的答案。没有一种方法永远是正确的,也没有一个决定在所有情况下都行得通。实践架构就是了解你的可用选项,权衡每个选项的积极和消极结果,然后做出决定。记录这些决定,以及进入这些决定的推理,是作为一个架构师的另一个重要部分。至关重要的是,我们和其他人都可以从成功和失败中吸取教训。
有一系列实践通常是决策制定中的良好默认。这并不是说它们总是正确的答案,而是在没有任何令人信服的相反理由的情况下使用它们通常会产生好的结果。
不要重复你自己
通常被称为 DRY,不要重复自己表明重复可能是一种反模式。当一段代码在一个项目中重复十次时,这意味着我们必须在这段代码发生变化时更新十个地方。如果我们在未来的更新中只更新其中的八个地方,我们可能会发现难以诊断的错误在我们认为它们已经被修复后仍然存在。
CSS 也是如此——随着时间的推移,重复相同的规则集和声明会导致额外的维护工作和外观上的不一致。
有许多机制可以减少样式表中的重复,包括级联、继承、变量和混合。
奥卡姆剃刀
奥卡姆的逻辑剃刀是:“不要在没有必要的情况下增加实体!”。虽然奥卡姆从未写下这些确切的词语,但这一原则来自他在解决问题时的工作,使其与编程环境相关。奥卡姆剃刀原理更广为人知的说法可能是“最简单的工作方案可能是最好的方案。”
Note
逻辑剃刀是一种理性的原则,用来刮掉对给定现象可能但不现实或不太可能的解释。 9
简单性为我们的代码提供了巨大的价值。它可以让代码更容易调试,更容易阅读,也让新队友更容易上手。此外,这提供了一个排除任何外部因素的极好的缺省值——我们能想到的最简单的解决方案应该足以应对许多情况。
你不需要它
有时被称为 YAGNI,你不会需要它的原则是,我们通常应该避免在代码中添加任何我们没有特定需求的东西。一般来说,我们应该尽可能保持代码简单,以避免过早优化,除非有令人信服的理由。通常,这甚至意味着忽略 DRY 原则,直到我们知道我们将需要三到四次或更多的代码,因为最小化重复的成本对于仅仅两到三个案例来说可能太昂贵了。
向他人学习
使用现有的架构模式和方法,比如在第 10 章中介绍的那些。使用谷歌找到其他有类似挑战的人,并向他们学习。利用社交媒体获得同事的帮助。
网络架构
如前所述,web 页面通常由文档(HTML)、样式表(CSS)和可能的脚本(JavaScript)组成,所有这些都通过用户代理(web 浏览器)提供给最终用户。web 浏览器执行许多活动来从这些组件构建网页。Mozilla Firefox 模型如图 1-3 所示。
图 1-3
浏览器引擎 10
所有的源文件都必须从 web 服务器上获取,然后文本必须根据其类型进行解析。HTML 和 JavaScript 结合起来构建和操作文档对象模型(DOM ),这将在下文中更详细地描述。样式引擎将 DOM 和 CSS 结合起来生成布局,包括任何媒体文件,如图像或视频。但是,即使这个布局也只是一个非可视的模型,必须使用绘制和合成步骤将其呈现在屏幕上。
虽然没有必要完全理解浏览器进行的所有活动,但 HTML 和 CSS 之间的关系是本书特别感兴趣的。因为我们已经解释了 CSS,所以在接下来的章节中提供了 HTML 和 DOM 的概述。
超文本标记语言
为了让 CSS 在 web 环境中工作,必须从 HTML(超文本标记语言)文档中引用所需的样式或样式表。有三种可能的选择,但是大多数情况下最好的方法是链接到一个外部样式表文件,如清单 1-1 所示。
<!DOCTYPE> <html> <head> <title>Linked Style Sheet</title> <link rel="stylesheet" href="styles.css"> </head> <body> <p>Sample HTML</p> </body> </html> Listing 1-1Link to External Style Sheet
在一些罕见的情况下,HTML 是独立的,并且在一个文件中包含所有的样式信息,这可能是必要的或可取的。这可以通过使用清单 1-2 中所示的样式标签来实现。
<!DOCTYPE> <html> <head> <title>Embedded Style Sheet</title> <style> p {
font-weight: bold; } </style> </head> <body> <p>Sample HTML (in bold)</p> </body> </html> Listing 1-2Self-Contained Styles
最后一种方法是在 HTML 标签中直接内联包含样式,如清单 1-3 所示。使用该方法在功能上等同于直接使用 JavaScript 设置元素样式。
<!DOCTYPE> <html> <head> <title>Inline Styles</title> </head> <body> <p style="color: red">Sample HTML (in red)</p> </body> </html> Listing 1-3Inline Styles
有大量的 CSS 特性是使用内联样式无法实现的,包括大多数 at-rules。此外,这“打破”了级联和继承,这在第三章中有更深入的描述。
HTML 文档必须指定自己的样式表,这一事实意味着文档与样式表之间存在权威关系。样式表不能指定它属于什么样的文档,但是它可以指定选择器和条件来决定规则应用于文档的情况,这个概念将在后面的章节中详细阐述。
由于 CSS 和 HTML 之间的关系,理解图 1-4 中概述的 HTML 文档的结构和词汇很重要。
图 1-4
HTML 结构
如前所述,HTML 由尖括号分隔的标签组成。HTML 元素是指标签的全部内容,从开始标签的第一个尖括号到结束标签的最后一个尖括号。有些元素,比如<img>
,没有主体,因此没有结束标签。一些元素,如<div>
或<button>
,可能在它们的开始和结束标签之间包含文本甚至其他标签。所有标签都可能有属性,如ID
、class
或title
。一些标签具有强制属性,这些属性被认为是有效的。
标签、属性和值都有 CSS 选择器,这将在第二章中详细介绍。这里值得注意的一点是,一些 HTML 标签的存在主要是为了提供语义上下文。
Note
语义学是语言学和逻辑学中有关意义的分支。当应用于代码(包括 HTML)时,我们使用“语义”一词来表示一个单词或标签,它传达的意义或目的不仅仅是一个简单的标签。
例如,<div>
可以用来对任意一组标签进行分组,但这只是一个普通的划分或分组。<nav>
标签表示导航,<article>
表示独立自足的内容,<aside>
表示相关但次要的内容,很像前面的注释。这个额外的含义对用户代理和屏幕阅读器很有帮助,但也可以在我们的 CSS 中使用,以编写更健壮和更有意义的选择器。
文档对象模型
文档对象模型(DOM)是由用户代理构建的关系树,它从一个或多个来源(包括 HTML、JavaScript 和 CSS)描述整个文档。DOM 规范包括一个用于 JavaScript 访问和操作的 API,每个 HTML 元素和属性都映射到 DOM 上,如图 1-5 所示。
图 1-5
文档对象模型
DOM 中的每一项都称为一个节点。节点可以是元素、属性或文本,反映了底层的 HTML。和 HTML 一样,元素也有属性。所有这些都可以使用动态网页的 JavaScript 直接读取和修改。
注意 CSS 并不直接影响 DOM。然而,通过直接修改class
或style
属性,可以使用 DOM 来改变可视化输出。回头看图 1-5 ,我们看到 DOM 提供了样式引擎在生成布局时应用 CSS 的结构。
CSS 的历史
| 十月浏览器:万维网,蒂姆 berners-Lee–第一款网络浏览器 | One thousand nine hundred and ninety | | | | One thousand nine hundred and ninety-three | 1 月 23 日浏览器:Mosaic——第一个显示内嵌图像和文本的浏览器 | | 10 月 1 日万维网联盟(W3C)成立十二月十五日浏览器:网景浏览器 1 | One thousand nine hundred and ninety-four | 十月十日由 kon Wium Lie 首先提出的 CSS | | 8 月 16 日浏览器:Internet Explorer 1.0 | One thousand nine hundred and ninety-five | 4 月 10 日浏览器:Opera 1 | | | One thousand nine hundred and ninety-six | 十二月十七日级联样式表,级别 1 (CSS1)推荐,W3C | | 5 月 12 日一份名单,杰弗里·泽尔德曼 | One thousand nine hundred and ninety-seven | | | 八月网络标准项目(WaSP) | One thousand nine hundred and ninety-eight | 5 月 12 日级联样式表,级别 2 (CSS2)W3C 推荐标准 | | | One thousand nine hundred and ninety-nine | 6 月 22 日前 3 个 CSS3 草稿:颜色配置文件、多栏布局和分页媒体 | | 4 月 14 日 CSS3 简介,W3C 工作草案 | Two thousand | | | | Two thousand and two | 十月十日连线新闻 CSS 重新设计
11 | | 1 月 7 日浏览器:Safari 15 月 7 日 CSS 禅宗花园,戴夫谢伊 | Two thousand and three | 二月 ESPN CSS 改版
12 | | | Two thousand and four | 11 月 9 日浏览器:火狐 1.0 | | 十月 Sass CSS 预处理程序 | Two thousand and five | | | | Two thousand and seven | 7 月 4 日 CSS-Tricks,Chris Coyier | | 十二月十一日浏览器:谷歌浏览器 1.0 | Two thousand and eight | | | | Two thousand and nine | 八月较少的 CSS 预处理程序 | | 四月 caniuse.com | Two thousand and ten | | | | Two thousand and eleven | 6 月 7 日级联样式表,级别 2 修订版 1 (CSS 2.1),W3C 建议 | | 6 月 19 日媒体查询,W3C 推荐
13 | Two thousand and twelve | | | | Two thousand and thirteen | 11 月 7 日样式属性 W3C 推荐
14 | | 3 月 20 日 CSS 形状模块级别 1W3C 候选人推荐
15 | Two thousand and fourteen | | | | Two thousand and fifteen | 12 月 3 日级联变量的 CSS 自定义属性 W3C 候选人推荐
16 | | 3 月 1 日 CSS 灵活框布局级别 1,W3C 候选人推荐
17 | Two thousand and sixteen | 9 月 29 日 CSS 网格布局模块级别 1,W3C 候选人推荐
18 | | 9 月 25 日滚动条模块级别 1,W3C 首次公开草案
19 | Two thousand and eighteen | 11 月 6 日选择器级别 3,W3C 推荐
20 | | 十一月二十四日书写模式级别 3W3C 提出建议
21 | Two thousand and nineteen | |
CSS 的创建
影响网页的视觉风格是一个众所周知的问题。最初,一些基本的可视化控件被内置到 HTML 中,但是这种方法的问题很快就被发现了。正如本章开始时提到的,许多人提出,甚至实现了设计网页样式的机制,在此期间,kon Wium Lie 提出了 CSS 的想法。他和伯特·波斯一起制定了一个提案,并提交给了新成立的 W3C。
Lie 和 Bos 与克里斯·威尔逊和 Vidur Apparao 一起成立了第一个 W3C CSS 工作组,Chris Lilley 是第一任工作组主席。CSS 级推荐标准在两年后的 1996 年发布。
håkon wium lie 博士
1994 年,kon Wium Lie 加入了欧洲粒子物理研究所的万维网项目,在那里他加入了网络先驱蒂姆·伯纳斯·李和罗伯特·卡里奥。在第一年,他利用他在麻省理工学院媒体实验室的电子出版背景,提出了 CSS 的建议。第二年,他去了 W3C 的 CSS 工作组工作。
Lie 在 1999 年成为 Opera Software 的 CTO,Opera Software 是当时对 CSS 最友好的浏览器。他继续担任这一职务,直到 2016 年公司被出售。
-你好。伯特·博斯
当 kon Wium Lie 在做他的 CSS 提案时,伯特·波斯正在制作他自己的基于流的样式表提案。他审阅了 CSS 的初始方案,他和 Lie 确定这两个方案可以合并。在 1995 年万维网项目从 CERN 转移出来期间,Bos 受雇于新成立的 W3C,在那里他继续与 Lie 一起研究 CSS1 规范。
Bos 仍然是 CSS 工作组的活跃成员,之前曾担任该工作组的主席。他和 Lie 一起写了层叠样式表:为网络设计,这是关于 CSS 的最早的书籍之一。
克里斯·莉莉
Chris Lilley 作为从事 HTML 2.0 和 PNG 图形格式工作的互联网工程任务组(IETF)的成员,开始建立 web 标准。他于 1996 年加入 W3C,最初从事图形和字体方面的工作,主持一个 Web 字体工作组。一年后,当 CSS 工作组成立时,他成为了这个工作组的主席。第二年,他开始担任 W3C 可缩放矢量图形(SVG)工作组主席,为期 10 年。多年来,Lilley 撰写和编辑了大量的 web 和图形规范,以及相关书籍。
克里斯·威尔逊
克里斯·威尔逊是第一个 CSS 工作组的创始人之一,他被 kon Wium Lie 誉为在规范完成之前就将 CSS 添加到 Internet Explorer 第 3 版的程序员。从那以后,他一直活跃在 W3C 中,他担任的职务包括 Web 平台孵化小组主席、HTML 工作组主席和顾问委员会成员。他在 2009 年之前一直为微软开发 Internet Explorer,2010 年他加入了谷歌,在那里他负责 Chrome,特别是增强和虚拟现实功能。
真空出现了
Vidur Apparao 在 Netscape 担任首席架构师时加入了最初的 CSS 工作组,当时他正在开发 Gecko 布局引擎。除了在 CSS 组的工作之外,他还参与了文档对象模型的推荐工作。在做了十多年的 web 架构师之后,Apparao 继续他的云软件高管生涯。
早期采用
在 CSS 成为众所周知的成熟技术之前,一些早期的网站需要冒险用内嵌样式更新他们的旧 HTML3 网站,使其布局和主题更加“纯粹”。
这些非常公开的网站迁移的第一个是有线新闻。2002 年 10 月 11 日,Wired News 宣布了他们的重新设计,包括符合 web 标准和技术,包括 XHTML 和全 CSS 布局。埃里克·迈耶说过:
- 这种新设计在 Web 服务器本身上更容易访问、下载更快、更灵活、更容易。任何对网络未来感兴趣的人只需要看看这个就够了。 24
当《连线》杂志发布他们的重大公告时,ESPN 的另一个团队正在努力开发他们的新网站。仅仅 4 个月后宣布,他们的巨大胜利是(几乎)无表布局。 25 通过证明使用基于 CSS(而不是基于表格)的布局来构建网站可以为月浏览量达到数百万的网站服务, 26 这两个网站帮助巩固了 CSS 作为强大的网络标准的地位。
早期倡导者
如果没有早期的倡导者来通知和教育 web 开发人员,我们今天可能会有一个非常不同的 Web。大量的开发人员,包括本书的作者,都受到了这些倡导者的教育和启发,本书并不是孤立的,而是建立在他们多年工作的基础上。
名单之外
web 和 CSS 教育和宣传的第一次重大努力是从电子邮件列表开始的。杰弗里·泽尔德曼于 1997 年创立了一个名为“与众不同”的组织,埃里克·迈耶很快加入其中。这个早期的邮件列表已经发展成为一个完整的生态系统,包括书籍和会议,直到今天仍然具有影响力。
泽尔德曼
杰弗里·泽尔德曼在 1995 年开始了他的网页设计生涯,此前他从事了十年的广告文案工作。在创立 List Apart 的第二年,他与乔治·奥尔森和格伦·戴维斯共同创立了 web 标准项目(WaSP ),开始了对开放 Web 标准的长期推动。泽尔德曼于 2012 年入选 SXSW 交互名人堂,是第一个获此殊荣的人。 27
埃里克·迈尔
Meyer 与 kon Wium Lie 和 Tim Boland 一起开发了 CSS1 的首个测试套件,旨在帮助评估是否符合该标准。同年,他加入 WaSP,并共同创建了 CSS 行动委员会。Meyer 已经写了六本关于 CSS 的书,并且为一些最有影响力的网页设计出版物写了无数的文章,包括一个列表。他还创建了 CSS-discuse 邮件列表,并与杰弗里·泽尔德曼共同创建了一个活动。2006 年,Meyer 因其在 HTML 和 CSS 方面的国际工作而入选国际数字艺术与科学学院。
CSS 禅宗花园
2003 年,一个神奇的新网站启动了,展示了 CSS 的力量。CSS Zen Garden 有一个独特的方法–
它提供了一个固定的 HTML 文档,设计者被鼓励使用 CSS(和图片)来设计他们想要的风格和主题。通过阻止对 HTML 的编辑,网页设计者被迫分离他们的设计实现,结果是不可思议的。最初的几个主题是由网站作者 Dave Shea 提供的,但是很快全世界的设计师都提交了他们的主题供考虑。这提供了一个强大的动手实验室,一劳永逸地证明了 CSS 作为网络生态系统的一等公民的地位。
2005 年,Dave 与 Molly Holzschlag 合作出版了一本书,书名为CSS 设计之禅,这本书卖出了 70,000 多册,成为网页设计的国际标准。 28
戴夫·谢伊
在推出 CSS Zen Garden 之前不久,Dave Shea 开始了一个关于网页设计的博客,名为 mezzoblue 。在接下来的几年里,他成为了一名多产的博客作者,就广泛的话题提供了有价值的见解。Shea 在 Web 标准项目中很活跃,也为一个独立的列表写文章。 29
茉莉木版
当欧洲粒子物理研究所构思网络时,莫莉·霍尔茨施拉格开始了她在互联网技术方面的职业生涯。1996 年,她出版了第一本关于网页设计的书,之后又写了超过 35 本关于网页技术和设计的书。她被广泛认为是网络上最有影响力的女性之一。
Holzschlag 直接与 CERN、AOL、微软、BBC、易贝、Opera 和 Netscape 合作,确保浏览器支持现代标准。她是 WaSP 的项目负责人,W3C CSS Accessibility Community Group 的主席,也是国际化指南、教育和推广工作组以及 HTML 工作组的 W3C 特邀专家。 三十
CSS 技巧
阅读这本书的人不太可能不通过 Chris Coyier 创建的 CSS-Tricks 网站来寻找 CSS 的答案。十多年来,这个网站一直在分享关于 CSS 和其他 web 开发主题的实用技巧和诀窍。
克里斯科伊耶
2007 年,克里斯·科伊尔创建了 CSS-Tricks,作为一个关于 CSS 的个人博客。今天,网站上有大量 web 开发人员和设计人员的文章,包括本章列出的许多人。与蒂姆·萨巴特和亚历克斯·瓦兹奎一起,科伊尔创立了 CodePen,这是一个非常受欢迎的在线代码编辑器和共享平台。 31
今日 CSS
W3C 的 CSS 工作组在出色的领导下仍然很强大。当前 CSS level 3 的模块化方法,以及常青浏览器的新趋势,导致了稳步的进步。以下是一些活跃的有影响力的人,除了已经提到的那些人之外,他们正在继续改善 CSS 的状况。
雷切尔·安德鲁
Rachel Andrew 是 20 多本关于 web 开发书籍的作者。她是 WaSP 的成员,也是 W3C CSS 工作组的特邀专家。她是一名谷歌开发专家,一份独立列表的撰稿人,以及 Smashing 杂志的主编。 32
珍·西蒙斯
Jen Simmons 是 Mozilla 的一名设计师和倡导者,她负责 Firefox,特别是 Grid Inspector。她曾在许多会议上发言,包括一个单独的事件和 SXSW。Simmons 是 W3C CSS 工作组的活跃成员,她在 CSS 网格布局的设计和部署方面影响极大。自 1998 年以来,她一直是一名活跃的 web 开发人员,她的客户包括 CERN、W3C 和 Google。 33
妮可·沙利文
妮可·沙利文是一个受欢迎的演讲者,她的会议露面包括一个事件除了和 SXSW。她合著了两本关于 web 性能的书,并且是 CSS 和 web 标准的倡导者。Sullivan 开始了面向对象的 CSS (OOCSS)项目,它为 CSS 提供了一个架构框架。她还和 Nicholas Zakas 一起创建了 CSS Lint,一个帮助捕捉常见 CSS 错误的工具。 34
米丽娅姆·苏珊娜
Miriam Suzanne 是一名项目经理、用户体验设计师和前端架构师。作为一名多才多艺的作家和小说家,她撰写了《Jump Start Sass》一书,并且是 CSS Tricks 的专职撰稿人。Suzanne 是 Sass 核心团队的成员,是流行的开源工具(包括 Susy、True 和 Herman)的创建者。她是 W3C CSS 工作组的特邀专家,也是 Mozilla Developer channel 的教师,为 web 专业人员制作资源,包括工具、视频、文章和演示。Suzanne 是一名国际会议演讲者,2017 年她在 CSS Dev 大会上获得了“最佳”演讲者奖。 35
摘要
在这一章中,你已经了解了 CSS 的历史,它是如何发展成为一种编程语言的,以及 CSS 如何适应网页的构建。具体来说,您已经了解了:
- CSS 规则集各部分的名称
- CSS 是一种编程语言,为什么这很重要
- 用户代理(如 web 浏览器)如何将 CSS 应用于网页
在下一章中,你将回顾 CSS 语言的基本特性,特别关注高级的和不常用的语言特性。
伯特·波斯(2016 年 12 月 17 日)。2016 年之前的 CSS 简史。万维网联盟。检索 2019 年 8 月 9 日,来自 www.w3.org/Style/CSS20/history.html
2
万维网联盟。HTML & CSS。检索 2019 年 7 月 30 日,来自 www.w3.org/standards/webdesign/htmlcss
3
劳拉·申克(2019 年 5 月 25 日)。CSS 图灵完成了吗?检索 2019 年 7 月 31 日,来自 https://notlaura.com/is-css-turing-complete/
4
埃德格·W·迪杰斯特拉(1974 年 8 月 30 日)。论科学思维的作用。检索 2019 年 8 月 12 日,来自 www.cs.utexas.edu/users/EWD/transcriptions/EWD04xx/EWD447.html
5
W.P. Stevens、G. J. Myers 和 L. L. Constantine,“结构化设计”,载于 IBM 系统杂志,第 13 卷,第 2 期,第 115-139 页,1974 年。doi: 10.1147/sj.132.0115
6
RationalWiki。(2019 年 7 月 2 日)。技术术语。检索 2019 年 7 月 30 日,来自 https://rationalwiki.org/wiki/Technobabble
7
科芬,j .(2015 年 12 月 16 日)。正交、面向对象编程的原理。从 https://medium.com/@jasoncof/9bf1eb92a2e5 检索到 2019 年 7 月 30 日
8
乔纳森·谢弗(2015)。如果没有必要,什么不能相乘,《澳大拉西亚哲学杂志》, 93:4,644–664,doi:10.1080/000
9
https://rationalwiki.org/wiki/Logical_razor
10
potch(2017 年 5 月 9 日)。量子近距离:什么是浏览器引擎?Mozilla 黑客。检索 2019 年 8 月 12 日,来自 https://hacks.mozilla.org/2017/05/quantum-up-close-what-is-a-browser-engine/
11
www.holovaty.com/writing/136/https://stopdesign.com/archive/2002/10/11/finally-were-live.html
12
www.holovaty.com/writing/192/
13
www.w3.org/standards/history/css3-mediaqueries
14
www.w3.org/standards/history/css-style-attr
15
www.w3.org/standards/history/css-shapes-1
16
www.w3.org/standards/history/css-variables-1
17
www.w3.org/standards/history/css-flexbox-1
18
www.w3.org/standards/history/css-grid-1
19
www.w3.org/standards/history/css-scrollbars-1
20
www.w3.org/standards/history/selectors-3
21
www.w3.org/standards/history/css-writing-modes-3
22
http://web.archive.org/web/20000817100343/http://odur.let.rug.nl/~bert/stylesheets.html
23
https://dev.opera.com/articles/css-twenty-years-hakon/
24
www.holovaty.com/writing/136/
25
www.holovaty.com/writing/192/
26
https://stopdesign.com/archive/2002/10/11/finally-were-live.html
27
www.austinchronicle.com/screens/2012-03-02/where-no-man-has-gone-before/
28
http://daveshea.com/projects/zen-book/
29
http://thewebahead.net/guest/dave-shea
30
www.computerhope.com/people/molly_holzschlag.htm
31
https://chriscoyier.net/
32
https://rachelandrew.co.uk/
33
https://aneventapart.com/speakers/jen-simmons
34
https://aneventapart.com/speakers/nicole-sullivan
35
www.miriamsuzanne.com/who/
二、规则和选择器
虽然你可能已经熟悉了 CSS 的基础知识,但是这一章提供了一个在你做架构决策时可以使用的语言特性的快速概述。软件架构的一个重要部分是对工具和方法的深刻理解,这些工具和方法可用来完成各种任务,以实现我们的系统目标。
选择器
正如我们在第一章中看到的,选择器是 CSS 规则集的一部分,它决定了哪些元素应用了样式声明。熟练地使用选择器有助于分离 HTML 和 CSS,从而创建健壮且风格一致的网站和 web 应用程序。
基础
基本选择器允许根据元素在 HTML 中呈现的明显特征来选择元素:标记名、属性和类名。CSS 选择器语法非常有表现力,以至于有一个 DOM 函数querySelector
接受 CSS 选择器字符串来定位 DOM 树中的元素。参见第八章了解更多关于 JavaScript 的内容。
通用选择器
CSS 中的*
是匹配页面上每个元素的通用选择器。有时这可能是有帮助的,例如清单 2-1 和图 2-1 ,它为用键盘选择的任何元素添加了一个可视指示器。
图 2-1
通用选择器
*:focus { outline: 1px dotted grey; } Listing 2-1Outline Selected Elements
然而,这种便利是有代价的,通用选择器有效地缩短了关联声明的继承。
除非您有特定的用例,否则通常最好避免通用选择器,而支持继承。将通用选择器与其他选择器结合使用也是一个好主意。
通用选择器的一个用例是将声明应用于另一个元素的所有子元素,即使该属性不会被继承,例如 border。对于这个用例,可以考虑将定制属性或混合作为这种方法的替代。
类型选择器
在 CSS 中选择一个元素就像使用标记名一样简单。这叫做类型选择器,所有的 HTML 标签都是有效的选择器。
清单 2-2 和图 2-2 中的示例为所有段落元素添加了填充。
图 2-2
类型选择器
p { padding: 0.5rem; } Listing 2-2Add Padding to Selected Elements
类别选择器
要按类选择元素,只需使用一个点,后跟类名,如.example
。因为一个 HTML 元素可以有多个类,所以可以组合多个类选择器,它们也可以与元素选择器组合。参见列表 2-3 和 2-4 。输出如图 2-3 所示。
图 2-3
类别选择器
img { width: 200px; } button { background-color: lightblue; } button.outline { border: 1px solid green; } button.outline.bold { border: 5px solid darkgreen; } Listing 2-4Class Selector CSS
<body> <div> <p>Lorem Ipsum...</p> <img class="outline" src="image.png" alt="art"> <button class="outline">Cancel</button> <button id="ok" class="outline bold">OK</button> </div> </body> Listing 2-3Selector HTML
在这个例子中,由于类型选择器的原因,<img>
元素没有接收到边框。“确定”按钮的边框比“取消”按钮更粗、更深。
ID 选择器
ID 选择器#
根据元素的ID
属性选择元素。请注意,在单个页面上使用重复的 id 对于 HTML 是无效的,因此该选择器应该匹配 0 或 1 个元素。清单 2-5 中的例子使用了清单 2-3 中上一个例子的 HTML。图 2-4 显示了输出。
图 2-4
ID 选择器
img { width: 200px; } #ok { font-size: 1.5rem; font-weight: bold; } p#ok { color: pink; } Listing 2-5ID Selector CSS
这将使“确定”按钮上的按钮文本加粗。这个例子显示了 ID 选择器可以像类选择器一样与类型选择器组合。在这个例子中,通过以不匹配 HTML 的方式组合这些选择器,段落标记的内容不会变成粉红色。
属性选择器
属性选择器根据元素的一个属性匹配元素。这个选择器使用方括号来包含属性匹配,并且可以选择与类型选择器结合使用。例如,a [rel]
可以用来匹配所有具有给定关系的锚标签。为了允许<area>
标签也匹配,单独使用[rel]
。
除了测试属性的存在,这个选择器还可以测试特定的值,如清单 2-6 和 2-7 以及图 2-5 所示。
图 2-5
属性选择器
label, input, a, button { display: block; margin-bottom: 1rem; } button { display: flex; align-items: center; } /* Matches password input fields */ input[type="password"] { color: red; } /* Strikes out any quotes cited from Wikipedia */ blockquote[cite*="wikipedia.org"] { text-decoration: line-through; } /* Underlines any element with a title attribute containing the word "continue" with any Capitalization. */ [title*="continue"] i { text-decoration: underline; } /* Display a gray border around any input which has an accept starting with image, such as image/png */ input[accept^="image"] { border: solid 4px gray; } /* Display a PDF icon beside any .pdf download links */ a[href$=".pdf"]::before { content: url(icon-pdf.png); } /* Matches a material design icon such as <i class="material-icons">arrow_back_ios</i> */ i[class|="material-icons"] { color: blue; width: 32px; } Listing 2-7Attribute Selector CSS
<body> <h1>Attribute Selector</h1> <form> <button href="" title="go back"> <i class="material-icons">arrow_back_ios</i> Previous </button> <label> Username <input type="text" > </label> <label> Password <input type="password" > </label> <label> Avatar <input type="file" accept="image/png"> </label> <button href="" title="Continue"> Next <i class="material-icons">arrow_forward_ios</i> </button> </form> <blockquote cite="w3.org"> The World Wide Web Consortium (W3C) is an... </blockquote> <blockquote cite="https://en.wikipedia.org/wiki/Wikipedia"> Wikipedia is a multilingual online encyclopedia... </blockquote> <a href="myfile.pdf" download>PDF File</a> <a href="myfile.docx" download>Word Doc</a> </body> Listing 2-6Attribute Selector HTML
因为class
和ID
都是 HTML 属性,所以类和 ID 选择器具有等同的属性选择器,如表 2-1 所示。
表 2-1
属性选择器等效项
| |
基本选择器
|
属性选择器
|
| — | — | — |
| 按 ID 选择 | #contactForm
| [id=contactForm]
|
| 按类别选择 | .outline
| [class~="outline"]
|
Grouping
为了尽量减少声明块的重复,可以将选择器组合到一个逗号分隔的列表中。例如,a, button { ... }
将声明块应用于 HTML 中的锚和按钮元素。
组合子
我们已经看到了如何将类型选择器与类和 ID 选择器结合起来,但是如果我们想要结合多个类型选择器甚至属性选择器呢?还有一些其他的组合子可以实现这一点,它们甚至提供了基于 DOM 中元素关系的层次上下文。表 2-2 中的组合子示例可以在清单 2-8 和 2-9 以及图 2-6 中找到。
表 2-2
组合子
|
名字
|
配合
|
例子
|
描述
|
| — | — | — | — |
| 后裔 | " " (space)
| nav a
| nav 元素内的所有锚标记 |
| 儿童 | ">"
| nav > ul > li
| 导航列表中的第一个列表项,忽略第一级之后的任何项 |
| 兄弟 | "~"
| p ~ p
| 共享同一父元素的所有段落(在第一个段落之后) |
| 相邻兄弟姐妹 | "+"
| h2 + p
| 同一层次上紧跟在<h2>
标签之后的所有段落 |
图 2-6
组合子
nav a { display: block; margin: 0 1rem; } nav > ul > li { border: solid 1px gray; display: inline-block; list-style-type: none; vertical-align: top; } p ~ p { color: purple; font-weight: bold; } h2 + p { font-family: sans-serif; } Listing 2-9Combinators CSS
<body> <h1>Combinators</h1> <nav> <ul> <li><a href="">Home</a></li> <li> <a href="">Combinators</a> <ul> <li>" " (space)</li> <li>></li> <li>~</li> <li>+</li> </ul> </li> </ul> </nav> <main> <h2>List of Combinators</h2> <p>There are a few other combinators to make this...</p> <ul> <li> " " (space) <ul> <li>nav li</li> <li>nav a</li> </ul> </li> <li>></li> <li>~</li> <li>+</li> </ul> <p>By combining selectors together we can select...</p> </main> </body> Listing 2-8Combinators HTML
通过将选择器组合在一起,我们可以根据元素在 HTML 文档中的自然位置和顺序来选择样式。这有助于我们将布局、主题和内容分离开来,以实现更易管理的规则集。
伪元素
伪元素允许您选择 HTML 文档中不存在的元素,但可以在屏幕上直观地显示出来。::first-letter
和::first-line
都选择元素中的一部分文本。
虽然::first-letter
的效果可以通过在想要的字母周围添加一个<span>
标签来重现,但是对于流畅的布局,除了::first-line
之外,实际上没有其他方法可以选择文本块的整个第一行。这是因为这个规则是在计算完布局之后应用的,这样浏览器就知道哪些单词应该受到规则的影响。参见清单 2-10 和 2-11 以及图 2-7 。
图 2-7
伪元素–
:::第一行和::第一级
p::first-letter { color: red; font-size: 3rem; line-height: 0; display: block; float: left; margin-top: .125rem; margin-right: .5rem; } p::first-line { color: red; } Listing 2-11Pseudo Elements CSS – ::first-line and ::first-level
<body> <h1>Pseudo Elements</h1> <p>Lorem ipsum dolor sit amet, consectetur...</p> <p>Cras id blandit risus. Nunc dictum, elit...</p> <p>Quisque euismod tempus erat, sit amet pharetra...</p> </body> Listing 2-10Pseudo Elements HTML – ::first-line and ::first-level
::after
和::before
伪元素使用content
属性根据特定标准插入内容(文本或图像)。我们在清单 2-12 和 2-13 以及图 2-8 中看到了这一点。
图 2-8
伪元素–
::before 和::after
a { display: block; } a::before { content: url(link.png); display: inline-block; margin-right: .5rem; vertical-align: middle; } a::after { content: ' (link)' } Listing 2-13Pseudo Elements CSS – ::before and ::after
<body> <a href>First Link</a> <a href>Second Link</a> <a href>Third Link</a> </body> Listing 2-12Pseudo Elements HTML – ::before and ::after
您曾经想要自定义输入元素上的占位符文本吗?你可以用input[type=text]::placeholder
来做这件事(见清单 2-14 和 2-15 以及图 2-9 和 2-10 )。
图 2-10
伪元素–
::背景
图 2-9
伪元素–
::占位符和::选择
input { box-sizing: border-box; border-radius: 4px; border: solid 1px gray; padding: .5rem 1rem; font-size: 1rem; width: 100%; } input[type=text]::placeholder { font-family: cursive; } ::selection { background-color: cornflowerblue; color: white; } ::backdrop { background: cornflowerblue; } Listing 2-15Pseudo Elements CSS – ::placeholder, ::selection, and ::backdrop
<form> <label> Username: <input type="text" placeholder="Example: user@email.com"> </label> </form> <video width="100%" height="250" controls> <source src="" type="video/mp4"> </video> Listing 2-14Pseudo Elements HTML – ::placeholder, ::selection, and ::backdrop
你有没有在一个网站上选择文本,并注意到选择的突出显示是在网站的品牌颜色中?这可以通过*::selection {background-color: cornflowerblue}
来完成。
全屏浏览模式下的背景可以使用::backdrop
自定义。
在前面的示例中也可以看到这两种情况。
Note
CSS 规范要求在伪元素前有一个双冒号前缀,比如::after
。然而,大多数浏览器支持只有一个冒号(:after
)的伪元素,而不会抛出错误。您可能会在遇到的样式表中看到这种用法,理解它的工作原理很重要。一般来说,我们推荐标准的双冒号前缀有两个原因:(1)它符合 CSS 规范,(2)它清楚地区分了伪元素和伪类。
伪类
伪类根据 HTML 文档中没有的信息选择元素。这可能包括状态或上下文元数据。
一些伪类使得基于用户交互调整样式成为可能。
- 当一个元素被悬停时匹配(比如使用鼠标)
:focus –
匹配用键盘(通过制表符)或鼠标(通过点击元素)选择的元素:active –
匹配一个正在被激活的元素(比如点击,同时按下鼠标键):target –
选择一个元素,该元素的 ID 与 URL 的片段(在#)之后的部分)相匹配
使用位置伪类可以很容易地以漂亮的格式显示表格数据。分别用tr:first-of-type
和tr:last-of-type
选择表格的第一行和最后一行。使用同样的技术,使用<td>
选择第一列和最后一列。使用tbody > tr:nth-child(even)
高亮显示每隔一行。
管理表单和显示有用的指示器可以使用以下一些伪类:
:in-range
、:out-of-range –
与定义范围相比的数值:placeholder-shown –
如果占位符文本当前可见:invalid
,:valid –
检查表单字段的验证状态,查看是否有错误和成功指示器:checked
、:indeterminate
、–
用于选择当前选中的复选框或单选按钮,或者无法确定选中的选项:default –
仅当该元素是一组元素中的默认元素时才匹配(如表单上的默认提交按钮或默认单选选项):disabled
、:read-only
、:read-write –
根据用户交互的可用性匹配表单字段的当前状态:optional
,:required –
根据字段所需的状态匹配字段
另一个重要的伪类是:not()
选择器,它选择不匹配选择器列表的元素。虽然许多伪类都定义了它们的反转状态(例如,:optional
与:required
),但是在许多其他场景中,否定也是有用的。例如,您可以通过使用article > *:not(img) { ... }
来选择<article>
的每个直接子标签,即而非和<img>
。
这些伪类中有许多为 CSS 提供了功能,否则在设计用户体验时需要 JavaScript 的参与。通过利用 CSS 实现上下文相关的 UI,我们将应用程序和视图逻辑分开,提高了网站和 web 应用程序的可维护性和性能。前面提到的一些伪类的例子可以在图 2-11 所示的清单 2-16 和 2-17 中找到。
图 2-11
伪类
table { border-collapse: collapse; margin-bottom: 1rem;; width: 100%; } tr { border-top: solid 1px lightgrey; border-bottom: solid 1px lightgrey; } tbody tr:nth-last-of-type(odd) { background: lightblue; } th, td { padding: .5rem 1rem; text-align: left; } form { margin-top: 2rem; } form > *:not(button) { border-radius: 4px; box-sizing: border-box; display: block; } label { margin-bottom: .5rem; } input { border: solid 1px lightblue; padding: .5rem 1rem; width: 100%; } input:hover, input:active { border-color: slategray; } input:invalid { border-left: solid 5px red; } input:valid { border-left: solid 5px green; } button { padding: .5rem 1.5rem; border: solid 1px lightblue; border-radius: 3px; background: white; margin-top: .5rem; } button:hover, button:active { outline: dotted 1px blue; outline-offset: 2px; } Listing 2-17Pseudo Classes CSS
<body> <h1>Pseudo Classes</h1> <table> <thead> <th>Name</th> <th>Email</th> <th>Zip Code</th> </thead> <tbody> <tr> <td>Jane</td> <td>jane@email.com</td> <td>15978</td> </tr> <tr> <td>John</td> <td>john@email.com</td> <td>11458</td> </tr> <tr> <td>Alex</td> <td>alex@email.com</td> <td>68978</td> </tr> </tbody> </table> <form> <label> Name: <input type="text" maxlength="20" required> </label> <label> Email <input type="email" maxlength="100" required> </label> <label> Zip Code: <input type="number" max="99999"> </label> <button type="submit">Submit</button> </form> </body> Listing 2-16Pseudo Classes HTML
声明
如果我们不对元素应用样式,那么选择元素没有任何好处。每个规则集的声明部分是为匹配元素指定单个样式属性及其值的地方。
性能
CSS 中的属性指的是可能受到影响的布局和样式的各个方面。许多属性只适用于某些元素,而不适用于其他元素。有时,属性的可见性将取决于元素的显示设置。例如,height
属性在带有display: inline
的元素上被忽略,但是在display: inline-block
上呈现。
有些 CSS 属性是许多单个属性的简写符号。考虑清单 2-18 中的例子,其中两个声明块产生相同的结果。
p { border-width: 2px; border-style: solid; border-color: #; } p { border: 2px solid #; } Listing 2-18Border Property
每个单独的边框属性都可以作为边框速记属性的可选值参数。其他一些速记属性包括background
、box-shadow
、font
、padding
、margin
和outline
。其中每一个都有一个不同的属性列表,并且它们有一个特定的属性提供顺序。在使用这些方法时,一定要查阅参考资料,直到您对每种方法的语法都很熟悉为止。
一些属性和值的组合可能会产生看似相似但实际上非常不同的结果。表 2-3 列出了其中一些,并解释了它们之间的区别。
表 2-3
属性歧义消除
|
第一个属性
|
第二财产
|
描述
|
| — | — | — |
| 边距:2px | 填充:px; | 边距在方框模型之外,相邻时可以折叠。填充物在盒子里面。 |
| 边框:2px 纯黑; | 轮廓:2px 纯黑; | 边框添加到框模型尺寸,并存在于边距和填充之间。轮廓位于边框之外,不占用盒子上的空间。 |
| 可见性:隐藏; | 显示:无; | 隐藏元素仍然存在于页面上,并且可以占用空间和接收事件。渲染树中不存在未有效显示的元素。 |
属性对于突出显示屏幕上不希望项目回流的元素非常有用。这通常用于结合:focus
伪类来突出显示元素。关于盒子模型的详细信息,以及与布局相关的属性,包括display
、grid
、flex
,请参见第四章。
对可用 CSS 属性和值的全面回顾超出了本书的范围。对于一个优秀的参考,我们推荐 Mozilla 的 MDN CSS 参考,可以在 https://developer.mozilla.org/docs/Web/CSS/Reference 找到。
单位
有许多 CSS 属性需要一个<length>
数据类型。该长度是一个标量(数字)值,带有相关的测量单位。选择正确的单位可以使一个漂亮的、流畅的、有反应的布局和一个在用户调整窗口大小或缩放时会中断的布局产生区别。正确的单位也会对实现特定布局所需的工作量产生巨大影响。
我们将讨论三种基本的单位类别。第一类包括在设计时建立的绝对度量。第二类是字体相关的,这意味着如果用户缩放页面或者改变默认的字体大小,这些值的含义会相对于彼此而改变。第三类包含相对于视口的长度,这意味着它们将相对于浏览器大小或用户设备上的特定显示而变化。
绝对的
px计算机图形学的传统计量单位;这仅适用于基于屏幕的显示。
以英寸到–
英寸。1in.
= 6pc
= 72pt
= 2.54cm
。这将是打印机上的真实英寸,但相对于屏幕的参考像素定义,而屏幕的参考像素是96px
,与屏幕分辨率无关。
PCPica。印刷字体中的传统度量单位。
点 –
点。印刷字体中的传统度量单位。
厘米厘米–
厘米。1cm = 10mm。请参阅前面关于打印机和屏幕的英寸说明。
毫米毫米–
毫米。
Note
绝对测量单位不会相对于font-size
等用户设置进行缩放。因此,使用这些单元(尤其是在屏幕上)可能会导致严重的可访问性问题,因此不推荐使用。
字体-相对
ch –
表示元素字体中“0”字符的宽度(包括字样和大小)。
ex –
表示元素字体中“x”字符的高度(包括字样和大小)。
em –
元素的计算font-size
。如果这个单元用在font-size
属性上,它将相对于继承的font-size
。
rem –
与em
完全相同,但总是相对于根元素的font-size
(对于 HTML 文档来说就是<html>
)。这是许多网页设计者的首选默认单位,因为它允许可管理的流畅布局,同时解决了可访问性问题。
视口-相对
vh –
等于视口高度的 1%
vw –
等于视口宽度的 1%
vmin等于vh
或vw
的较小的
VMAX等于vh
或vw
的较大的
百分率
许多 CSS 属性将接受一个<percentage>
或一个<length-percentage>
(意味着长度或百分比)。虽然rem
对于许多目的来说是最好的选择,尤其是那些与内容和可访问性相关的目的,但是百分比对于任何继承的大小都有效,包括字体相对大小、视图相对大小,甚至是绝对单位。
功能
虽然 CSS 不允许用户定义函数,但是有大量的函数可用于执行各种任务,其中一些描述如下:
形状 –
功能circle()
、ellipse()
、inset()
和polygon()
支持多种非矩形形状。与shape-outside
属性结合以将文本换行为特定形状,或者与clip-path
结合以裁剪图像或容器。
变换 –
变换函数数量众多,包括rotateX()
、scale()
、skewY()
。还有perspective()
、matrix3d()
、scaleZ()
等 3D 变换。这些变换可以调整屏幕上元素的形状、方向和位置,以创建各种视觉效果和布局。
渐变 –
有大量的函数支持渐变的创建,包括linear-gradient()
、radial-gradient()
、repeating-linear-gradient()
、repeating-radial-gradient()
。由渐变实现的颜色混合支持大量的视觉效果。
除了渐变,还有其他视觉效果。blur()
功能将在所选元素上产生高斯模糊,甚至是图像。这对于模式对话框的背景很有用。drop-shadow()
给主题增加了一些维度。并且opacity()
允许元素处于完全不透明和完全透明之间,以允许维度覆盖。(请注意,如果您想要不透明的文本和半透明的背景,您可以考虑使用rgba()
或hsla()
颜色功能,如下文所述。)
在 CSS 中指定颜色最常见的方式是在 3 或 6 位数的十六进制代码前加一个散列符号,比如红色的#FF0000
。也可以使用hsl()
和hsla()
功能通过色调、饱和度和亮度指定颜色,或者使用rgb()
或rgba()
指定为 RGB(红、绿、蓝)。每个函数集中的“a”指的是指定不透明度或透明度级别的 alpha 通道。
还可以使用filter
属性以一致的方式操纵颜色,修改如contrast()
、saturate()
和hue-rotate()
,应用效果如grayscale()
或sepia()
。这些函数特别有用,因为它们可以应用于页面上的图像和文本。
资源–``url()
功能用于通过 CSS 将图像资源添加到设计中。这使得 HTML 中的< img >标签可以保留给与内容相关的图片,而不是与布局和设计相关的图片。
计数 –
计数函数counter()
、counters()
、symbols()
用于管理计数器变量。有关计数器的更多信息,请参见下面的“变量”一节。
数学 –
有时候内置的单位不够,你需要根据其他元素来计算大小或位置。calc()
函数使得用混合单位做一些基本的数学运算成为可能。支持加、减、乘、除和括号。举个例子,你可以使用height: calc(10vh - 1rem)
来计算一个标题的高度,它是视窗高度的 10%,但是考虑到了1rem
边框。
清单 2-19 和 2-20 显示了图 2-12 的源代码。
图 2-12
功能
.shape { clip-path: polygon(50% 0%, 61% 35%, 98% 35%, 68% 57%, 79% 91%, 50% 70%, 21% 91%, 32% 57%, 2% 35%, 39% 35%); display: inline-block; position: relative; height: calc(100vw / 3); width: calc(100vw / 3); } .shape:nth-of-type(1) { background: rgba(255, 0, 255, 0.31); transform: rotate(-25deg); filter: saturate(15%); } .shape:nth-of-type(2) { background: rgb(255,116,0); background: linear-gradient(90deg, rgba(255,116,0,1) 0%, rgba(255,237,0,1) 47%, rgba(255,167,0,1) 100%); filter: opacity(.75); transform: translate(0, -50px); left: calc((100vw / 3) - 200px); } .shape:nth-of-type(3) { background: hsl(189, 100%, 50%); transform: rotate(25deg); opacity: .33; left: calc((100vw / 3) - 100px); top: -200px; } Listing 2-20Functions CSS
<body> <h1>Functions</h1> <div class="shape"></div> <div class="shape"></div> <div class="shape"></div> </body> Listing 2-19Functions HTML
图 2-12 中所示的例子突出了一些功能。示例中星星的位置取决于浏览器窗口的大小,因为计算基于vw
和vh
单位。
变量
有几种方法可以在 CSS 中使用动态数据(示例见清单 2-21 和 2-22 以及图 2-13 ):
自定义属性 –
这些变量的定义很像任何其他 CSS 属性,可以包含任何在 CSS 中合法的值。然后可以使用var()
函数在样式表中引用它们。
属性 –
使用attr()
函数,您可以从 HTML 属性中获取值。将它与content
属性结合起来,以独特的方式显示属性数据。
每个 HTML 元素可以有 0 到多个命名的计数器,这些计数器在文档树中相关联,并使用 CSS 进行操作。HTML 列表自动生成一个“列表项”计数器,除非显式重置,否则每个列表元素递增 1。这也包括无序列表。使用counter-set
、counter-increment
或counter-decrement
属性调整计数器,并使用counter()
或counters()
以您选择的方式显示指定计数器的值。这是为了支持嵌套列表而存在的,但可能有许多其他用途。
图 2-13
变量
ul { counter-reset: li; } li:before { content: counter(li)"-" attr(category)": "; counter-increment: li; text-transform: capitalize; background: lightblue; display: inline-block; padding: .5rem 1rem; border-radius: 25px; margin: 0 1rem 1rem 0; } Listing 2-22Variables CSS
<body> <h1>Variables</h1> <ul> <li category="fruit">Apple</li> <li category="vegetable">Lettuce</li> <li category="starch">Corn</li> </ul> </body> Listing 2-21Variables HTML
规则
CSS at-rules(之所以这样命名是因为每个名称中都有@或“at”符号)是一些语言特性,它们提供了对样式结构的一些控制。这些规则提供了一种收集或分组其他规则集的机制。
@导入
在第一章中,我们看了在 HTML 文档中包含 CSS 的三种方法,包括<link>
元素。@import
at-rule 为 CSS 提供了类似的功能。这两者都在 CSS 文件中包含了拉机制,有效地将其内容插入到 import 语句的位置。
这非常有用,因为它允许我们将样式表分解成更具逻辑性和更易管理的文件,而不会对 HTML 文档产生任何影响。关于@imports
和其他引入外部样式表的机制的更深入的讨论,请参见第七章。
@支持
@supports
at-rule 允许基于用户代理对 CSS 特性的特定支持来应用规则。这是一种基于 web 浏览器声明支持的内容来提供样式和格式的方式,而不是使用老派的技巧来尝试检测给定的规则是否会按预期工作。
这种 at-rule 允许您开始利用当今最新的 CSS,它使得为尖端浏览器提供替代规则(或者为旧浏览器提供替代规则)成为可能,如清单 2-23 和 2-24 中所示。
p { text-decoration: underline; text-underline-offset: 1rem; } @supports not (text-underline-offset: 1rem) { p { text-decoration: none; padding-bottom: 1rem; border-bottom: solid 3px orange; display: inline-block; } } Listing 2-24At-Rules CSS
<body> <h1>At-Rules</h1> <p>Hello World</p> </body> Listing 2-23At-Rules HTML
因为text-underline-offset
是 Firefox 支持的(图 2-14 ,Firefox 忽略了@support not
代码。然而,在撰写本书时,Opera 不支持text-underline-offset
,因此使用@supports not
中提供的回退代码(图 2-15 )。
图 2-15
Opera 中不支持 At-Rules,text-underline-offset
图 2-14
火狐支持 At-Rules,text-underline-offset
@媒体
CSS media at-rule 用于对系统、环境或用户代理执行查询。这种使用被称为媒体查询。
Media Query
媒体查询允许作者测试和查询用户代理或显示设备的值或特征,而与正在呈现的文档无关。它们用在 CSS @media 规则中,以有条件地将样式应用于文档,以及各种其他上下文和语言,如 HTML 和 JavaScript。
—媒体查询级别 4 1
如第四章所述,这些媒体查询可用于构建响应性布局,但它们还有许多其他用途。例如,可以创建一个打印机友好的主题,隐藏导航和横幅,同时保留内容,如清单 2-25 所示。
@media print and monochrome { nav, .banner { display: none; } } Listing 2-25Printer-Friendly Design
通过使用@page
at-rule 提供特定于页面的指令,可以获得对打印的额外控制。 2
还可以为没有支持悬停的定点设备(如鼠标)的设备调整布局,这在平板电脑和移动设备中很常见。在清单 2-26 中,我们在每个链接旁边显示目标 URL,但是只在不支持悬停的设备上显示。
@media (not(hover)) { a::after { content: attr(href); font-size: x-small; position: absolute; } } Listing 2-26Tablet-Friendly Icon
摘要
本章讲述了 CSS 规则集的基本构件。你已经学会了如何
- 指定打印机特定的布局和设计
- 仅使用 CSS 根据文件扩展名插入文档图标
- 将多个选择器组合成更高级的表达式
- 突出显示表格中的交替行
- 基于浏览器和设备功能提供替代样式
下一章将介绍级联样式表的级联部分,并解释用户代理决定页面上每个元素的每个属性值的过程。
媒体查询级别 4。(2019 年 8 月 9 日)。检索自 www.w3.org/TR/mediaqueries-4/
2
https://developer.mozilla.org/en-US/docs/Web/CSS/@page
三、重要性顺序
正如在第一章中提到的,CSS 的一个重要特性是用户、浏览器和 web 开发者都能够影响页面的最终输出。用户代理、作者和用户都可以影响页面的输出。为了规定什么属性值“胜出”,需要执行多步计算。
遗产
继承是一种机制,通过这种机制,CSS 允许父元素上设置的值(如<body>
)传播到其子元素。这有助于确定在元素属性上没有声明属性时使用什么值。继承的值由父代或祖先的计算值决定。如果不存在,则使用初始值或浏览器设置的默认值。
默认情况下,并非所有属性值都会被继承。 1 确实如此的属性通常与主题化相关,例如与排版相关的属性(字体大小、行高、字母间距等。).与布局相关的属性(如显示、边框、宽度和高度)通常不是。如果不可继承的属性没有声明的值,则使用初始值。见清单 3-1 和 3-2 和图 3-1 。
<body> <h1>Inheritance</h1> <p>Lorem ipsum dolor sit amet, consectetur... </p> <img src="image.png" alt="art"> <table> <tr> <th>Lorem Ipsum</th> <td>Lorem ipsum dolor sit amet, consectetur... </td> </tr> <tr> <th>Pellentesque</th> <td>Pellentesque sit amet massa auctor est... </td> </tr> </table> <p>Pellentesque sit amet massa... </p> </body> Listing 3-1Cascading and Inheritance HTML
body { color: gray; padding: 2rem; text-align: justify; line-height: 1.5rem; font-family: Helvetica, Arial, sans-serif; font-weight: lighter; } h1 { color: slategray; font-family: 'Comic Sans MS'; font-size: 2.5rem; letter-spacing: .0625rem; } h1 { font-family: fantasy; } p:first-of-type::first-letter { color: gold; display: block; float: left; font-size: 3rem; line-height: 0; margin: .5rem .5rem 0 0; } table { border-collapse: collapse; } tr { color: slategray; border-top: solid 1px lightsteelblue; border-bottom: solid 1px lightsteelblue; } td { padding: .5rem 1rem; } img { margin: 0 0 0 1rem; float: right; width: 200px; } Listing 3-2Cascading and Inheritance CSS
图 3-1
级联和继承
body 属性的text-align
属性值为justify
。段落属性上未设置样式;然而,这些段落事实上是合理的。段落的text-align
值继承自主体的text-align
属性。然而,填充不是继承的,这就是为什么即使正文选择器的填充值为两个 rem,段落、图像、链接等也没有两个 rem 的填充值。
继承的主要好处之一是,它避免了在不同的选择器中一遍又一遍地为相同的属性编写值的需要,有助于代码的风格一致性和可维护性。
在本例中,颜色也是继承的,但是第一段的第一个字母并没有像在body
选择器中设置的那样显示为灰色,而是像在p:first-of-type::first-letter
选择器中设置的那样显示为金色。第一段的第一个字母是金色而不是灰色的原因是一个特殊性的问题;p:first-of-type::first-letter
比body
更具体。
全球价值观
Inherit、unset 和 initial 与 CSS 中的其他属性值略有不同。这些值在所有属性上都可用,它们的明显区别是要么将值重置为默认值,要么重置为祖先值而不是新值。这些值使您可以明确控制属性的继承方式。
inherit
、unset,
和initial
的示例基于清单 3-3 和 3-4 中的代码。
body { font-family: sans-serif; padding: 10px; } p { padding: 20px; border: dashed 1px gray; } p::first-letter { display: block; float: left; font-size: 3rem; color: red; } p:nth-of-type(2) { padding: unset } p:nth-of-type(3) { padding: default } p:nth-of-type(3) { padding: initial } p:nth-of-type(4) { padding: inherit } li { font-family: cursive; } li:nth-of-type(2) { font-family: unset; } li:nth-of-type(3) { font-family: initial; } li:nth-of-type(4) { font-family: inherit; } Listing 3-4Exceptions CSS
<body> <h1>Exceptions</h1> <ol> <li>Cursive</li> <li>Unset</li> <li>Initial</li> <li>Inherit</li> </ol> <p>1 Lorem ipsum dolor sit amet, consectetur... </p> <p>2 Pellentesque sit amet massa auctor est... </p> <p>3 Duis vitae iaculis risus. Vivamus id egestas... </p> <p>4 Mauris vel mi quis lorem laoreet aliquet... </p> </body> Listing 3-3Exceptions HTML
未设置
根据所分配的属性,Unset 的工作方式会有所不同。如果值可以从父代继承,它将继承;否则,它会将属性值设置为 initial。
在列表项的情况下,由于font-family
可以被继承,第二个列表项将具有sans-serif
的font-family
。该值继承自其父容器body
(见图 3-2 )。
图 3-2
继承未设置
因为填充是不可继承的,所以填充在第二个段落标签上被设置为0
,因为段落标签上的初始填充值是0
(见图 3-3 )。
图 3-3
对不可继承的属性取消设置
最初的
属性的初始值可以由浏览器设置,并且可以根据用户代理而变化。如果 CSS 规范中声明了初始值,那么 initial 应该返回该值。大多数现代浏览器是一致的,但里程可能会有所不同。例如,在 Firefox 中,font-family
的默认值是serif
。因此,第三个列表元素font-family
的值为serif
(见图 3-4 )。
图 3-4
最初的
继承
属性值将等于父属性的值,无论该属性是否是默认继承的。填充不是继承的。即便如此,当在第四个段落标记的 padding 属性上设置 inherit 时,该段落标记采用设置为其父级<body>
的值。Body 的填充值为 10px 因此,该段也是如此。见图 3-5 。
图 3-5
继承
如这个例子所示,我们可以通过使用inherit
属性来强制继承,从而直接控制级联。
特征
根据计算特异性的方式,不同类型的选择器有不同的重要性顺序。表 3-1 中总结了四个重要类别,每个类别都比其下一个类别重要一个数量级。
表 3-1
选择器排名
|
种类
|
选择器
|
| — | — |
| A | ID 选择器 |
| B | 类选择器、属性选择器、伪类 |
| C | 类型选择器,伪元素 |
| Zero | 通用选择器 |
任何给定选择器的特异性计算为三位数,数字 A、B 和 C,其中 A、B 和 C 代表其类别中选择器的总数。 2 几个例子见表 3-2 。
表 3-2
计算特异性
|
示例选择器
|
A
|
B
|
C
|
特征
|
| — | — | — | — | — |
| *
| Zero | Zero | Zero | Zero |
| button
| Zero | Zero | one | one |
| ul li
| Zero | Zero | Two | Two |
| button:not([type=submit])
| Zero | one | one | 1 1 |
| a[href$=".pdf"]::before
| Zero | one | Two | 1 2 |
| button.outline.bold
| Zero | Two | one | 2 1 |
| button#submit
| one | Zero | one | 1 0 1 |
特殊性在决定级联过程中应用哪些样式时起着重要的作用。
Inline Styles
直接应用于 HTML 中元素的样式,如
<p style="margin-left: 10px">Lorem ipsum am met...</p>
是内嵌样式。它们相当于使用 JavaScript 直接在 DOM 中添加属性值。内联样式被赋予了[1 0 0 0], 3 的特异性,这比使用普通选择器可能得到的任何东西都高,如表 3-2 所示。内联样式通常被认为是不好的做法,因为它们忽略了继承和级联。但是也有一些不可避免的例外,包括 HTML 电子邮件。
优先
规则的应用顺序很重要。直接作为目标的规则总是优先于从父项或祖先继承的规则。如果应用了两个具有相同特征级别的规则,将应用最后一个规则。这个概念是 CSS 的核心,从一开始就有了,正如它的名字“级联样式表”所表明的。
!important
CSS 从业者都知道,!important
注释既是一个强大的工具,也是一个巨大的负担。当其他方式都不起作用时,web 开发人员有时会使用它来强制一种样式生效。但是你知道吗,!important
的目的实际上是为了提高可访问性。因为重要的用户声明总是具有最高的优先级,所以在呈现页面时,它让用户最终决定设置哪些属性和值。
除了特殊性,规则的来源也是确定元素使用的值的一个因素。表 3-3 按照从最不重要到最重要的顺序显示了层叠期间应用规则的顺序。
表 3-3
级联顺序 4
|
命令
|
起源
|
重要
|
优先
|
| — | — | — | — |
| one | 用户代理人 | 标准 | eight |
| Two | 用户 | 标准 | seven |
| three | 作者 | 标准 | six |
| four | 动画片 | | five |
| five | 作者 | !important
| 4
|
| six | 用户 | !important
| 3
|
| seven | 用户代理人 | !important
| 2
|
| eight | 过渡 | | one |
在层叠中,最后应用的项目获胜;所以,转场会争取用户代理!重要包含用户规则!重要的规则等等。
级联
级联表示来自各种源的属性和值以不同的优先级和特性级别组合在一起,以确定将呈现的最终样式集。
需要注意的是,级联到元素的是属性,而不是规则集。元素的最终状态可能包括在许多不同的规则集中声明的属性。
为了计算级联,应用以下公式: 5
- 选择具有最高优先级的声明。
- 选择剩余的具有最高特异性的声明。
- 当所有其他因素相同时,最后出现在的声明将被应用。
价值处理
所有不同来源的属性值被一起使用,以使用以下计算来确定最终值: 6
- 首先,对于每个元素上的每个属性,收集应用于元素的所有声明值。可能有 0 个或多个声明值应用于该元素。
- 级联产生级联值。每个元素的每个属性最多有一个级联值。
- 默认产生指定值。每个元素的每个属性只有一个指定值。
- 解析值依赖关系产生计算值。每个元素的每个属性只有一个计算值。
- 格式化文档会产生已用值。对于给定的属性,如果该属性适用于元素,则该元素仅具有已用值。
- 最后,基于显示环境的约束,使用值被转换成实际值。与使用的值一样,元素上的给定属性可能有也可能没有实际值。
W3C 规范中的这一计算引用了各种值分类,它们被定义为
- 声明的
–
这些都是与被检查的元素和属性相匹配的值(0–
多)。 - 级联
–
这是处理级联后选择的值(0–
1)。 - 指定
–
这是级联的值,如果可用,或者是属性和元素的默认值。对于每个属性和元素,始终有一(1)个指定值。 - 计算的
–
指定的值的绝对值,该值可以被子元素继承。 - 已用
–
这是用户代理用于文档布局的最终值。 - 实际
–
这是设备上实际显示的值,由于设备或环境的限制,可能会从使用的值进行调整。
每个元素的每个属性的最终实际值由项目代码以外的各种因素决定,包括设备、用户代理或浏览器、用户代理样式表和用户提供的样式表。
摘要
在这一章中,你已经学习了 CSS 如何从许多不同的来源获取规则集,并为网页建立一个应用样式的内聚集的细节。特别是,你学到了
- HTML 内联样式和
!important
注释如何影响层叠 - 如何计算任何给定选择器的特异性
- 在 DOM 树中继承属性的方式
在下一章中,你将了解 CSS 提供的不同选项,这些选项用于构建能够适应设备和内容变化的流畅的响应性布局。
www.w3.org/TR/CSS22/propidx.html
2
www.w3.org/TR/2018/REC-selectors-3-/
3
www.w3.org/TR/2018/REC-selectors-3-/#specificity
4
CSS 级联介绍:级联顺序。 MDN 网络文档。检索 2019 年 12 月 5 日,来自 https://developer.mozilla.org/en-US/docs/Web/CSS/Cascade
5
CSS 级联和继承级别 3:级联。W3C 。检索 2019 年 12 月 5 日,来自 www.w3.org/TR/css-cascade-3/#cascading
6
CSS 级联和继承级别 3:值处理。W3C 。检索 2019 年 12 月 5 日,来自 www.w3.org/TR/css-cascade-3/
四、布局
单个元素放在一起就形成了一个布局。使用 CSS,我们依靠盒子模型来控制每个元素的宽度和行为,而不需要布局。为了控制元素之间的相互关系,我们可以使用 display 和 float 这样的属性。在这一章中,我们定义了盒子模型,并着眼于具体布局的浮动、伸缩、内嵌块和网格。
盒子模型
布局内容的基础根植于框模型,它描述了为文档树中的元素生成的矩形框。如图 4-1 所示,内容由三个框包围:填充、边框和边距。
图 4-1
盒子模型
这些属性中的每一个,包括内容,都将由维度、类型、定位、与其他元素的关系以及外部信息来控制。
盒子尺寸
Box-sizing ,或者定义元素高度和宽度的属性,默认情况下有一个 content-box 的值,这意味着当为元素定义宽度和高度时,它只应用于内容。因此,向元素添加填充或边距会增加元素利用的总可用视区的百分比宽度。
内容盒
如果需要两列布局,每个 div 等于视口宽度的 50%,则需要从给定元素的宽度中减去应用于每列的填充量,否则两个元素的总宽度将超过 100%。
考虑包含两个 div 的 800 像素宽的视口。如果没有向 div 添加任何填充、边距或边框,并且每个 div 的宽度为 50%,则它们的组合宽度将等于视口的 100%。如果它们是浮动的,它们将完美地并排放置并占据 100%的屏幕,如图 4-2 所示。
图 4-2
内容框–
无填充
如果向列添加了填充,列的宽度将增加填充量,从而导致它们超过视区的宽度。图 4-3 显示了添加了填充的列。
图 4-3
带填充的内容框–
如果 div 是浮动的,那么第二个 div 将因此被推到第一个 div 的下面,因为它们的组合宽度现在大于容器的 100%,如图 4-4 所示。
图 4-4
使用box-sizing:
content-box
时填充和边框的效果
用代码
让我们把前面描述的场景放到代码中(清单 4-1 和 4-2;输出如图 4-4 所示。
.container { overflow:auto; } .container > div { width: 50%; float: left; } .container p { background: rgba(0, 0, 0, .16); /* light grey */ text-align: center; } .container > div:last-of-type p { /* second rectangle */ background: rgba(0, 0, 0, .32); /* dark grey */ } .has-padding > div { outline: dashed 1px rgba(0, 0, 0, .5); padding: 10px; } Listing 4-2CSS
<body> <h1>No Padding</h1> <div class="container"> <div> <p>Content</p> </div> <div> <p>Content</p> </div> </div> <h1>With Padding</h1> <div class="container has-padding"> <div> <p>Content</p> </div> <div> <p>Content</p> </div> </div> </body> Listing 4-1HTML
边框的行为方式与填充相同;因此,应用的任何边框宽度都需要包含在内容和填充的总和中,以计算布局中包含的元素的全宽或全高。
边缘塌陷
边距的行为与填充略有不同。当同级元素都有填充时,应用两者的填充,两个元素之间的间距是两组填充的总和。然而,根据具体情况,利润率可能会下降。页边距折叠是指顶部和底部页边距合并或折叠成一个页边距,等于应用的最大页边距,如图 4-5 所示,或者,如果所有页边距都为负值,则等于最大负值页边距的大小。左右边距不会折叠。
图 4-5
边距折叠
利润率将会崩溃
- 没有任何东西分隔父对象的边距和其子对象的边距,包括填充、边框、内联部分、块格式上下文或清除属性 clear(例如,
clear: right
,与 floats 一起使用)。 - 元素是相邻的兄弟元素,除非后者需要通过浮动来清除(本章后面会有更多关于浮动的内容)。
- 即使其中一个边距等于 0。 1
用代码
当两个各有 10 个像素填充的 div 并排设置时,它们之间只有 10 个像素的垂直边距(如清单 4-3 、 4-4 和图 4-6 所示)。
div { margin: 10px; background: rgba(0, 0, 0, .16); /* light grey */ } div:last-of-type { background: rgba(0, 0, 0, .32); /* dark grey */ } Listing 4-4CSS
<body> <div>Content</div> <div>Content</div> </body> Listing 4-3HTML
图 4-6
边缘塌陷
但是,如果我们以前面的例子为例,其中的列是浮动的,并替换了边距的填充,请注意边距没有折叠。当这些列的组合总宽度大于 100%[50%+(2x10px]x2 = 105%时,它们仍然会堆叠在一起,但是因为 div 是浮动的,所以边距不会折叠。见图 4-7 。
图 4-7
浮动 div–
无边距塌陷
与 border 不同,border 可以应用于任何类型的元素,而 margin 和 padding 对于它们可以应用于哪些元素都有限制。不能在 display 属性值为的元素上设置填充
- 表格-行-组
(<tbody>)
- 表格标题组
(<thead>)
- 表格-页脚-组
(<tfoot>)
- 表格行
(<tr>)
- 表格-列-组
- 表格列
不能在具有表格显示类型(如<tr>
、<td>
等)的元素上设置边距。)除了表格、内嵌表格和表格标题。 2
利弊
虽然混合像素和基于百分比的值可以产生一些有趣的数学结果,但是将box-sizing
值保持为content-box
的好处是,当宽度或高度值被分配给内容时,它不会受到添加的填充的副作用的影响。内容将完全是由开发人员指定的高度或宽度。此外,因为它是默认值,所以元素的大小将表现出“正常”的预期行为,而不必了解元素上已经设置的其他属性。
Special Case: Outlines and Box Shadows
轮廓和框阴影在边界周围创建框,可以认为属于框模型。事实并非如此,因为轮廓和方框阴影不占用空间。它们自身重叠,类似于图 4-8 中描述的绝对位置。
图 4-8
轮廓和方框阴影
轮廓和方框阴影不仅会覆盖下面的内容,而且会溢出视窗,无法滚动到视窗。因为它们不占用空间,所以不构成溢出,因此滚动条不会被触发。见图 4-9 和清单 4-5 和 4-6 。
.container div { background: rgba(0, 0, 0, .16); /* light grey */ height: 50px; } .container div:last-of-type { background: rgba(0, 0, 0, .32); /* dark grey */ } .container.outline div:last-of-type { outline: dotted 15px rgba(0, 0, 0, .5); } .container.box-shadow div:last-of-type { box-shadow: 0px 0px 10px 10px rgba(0, 0, 0, .5) } Listing 4-6CSS
<body> <h1>Outline</h1> <div class="container outline"> <div>Content</div> <div>Content</div> </div> <h1>Box-Shadow</h1> <div class="container box-shadow"> <div>Content</div> <div>Content</div> </div> <h1>Both</h1> <div class="container outline box-shadow"> <div>Content</div> <div>Content</div> </div> </body> Listing 4-5HTML
图 4-9
方框阴影和轮廓不占用布局中的空间
当轮廓和框阴影都设置时,它们将相互重叠。
边框
如前所述,box-sizing: content-box
在混合绝对单位和基于百分比的单位时有一些缺点。当内容有填充时,将内容设置为总宽度或高度的百分比时,内容框也会增加额外的复杂性。
这就是 border-box 出现的原因。分配box-sizing: border-box
改变元素的宽度和高度的计算方式。它不仅包含内容,还包含内容、填充和边框。当添加填充或边框时,内容本身的宽度和高度会因此而减小。见图 4-10 。
图 4-10
带衬垫的边框
因此,如果我们以两个浮动列的第一个示例为例,我们将看到这两个列保留了 50%的宽度。页边空白仍然以与content-box
相同的方式运行。清单 4-7 和 4-8 以及图 4-11 显示了与图 4-4 相同的示例,使用box-sizing: border-box
代替默认的box-sizing: content-box
。
图 4-11
使用边框的元素
.container { overflow:auto; } .container > div { width: 50%; box-sizing: border-box; float: left; } .container p { background: rgba(0, 0, 0, .16); /* light grey */ text-align: center; margin: 0; } .container > div:last-of-type p { /* second rectangle */ background: rgba(0, 0, 0, .32); /* dark grey */ } .has-padding > div { border: dashed 1px rgba(0, 0, 0, .5); padding: 10px; } .has-margin > div { border: dashed 1px rgba(0, 0, 0, .5); margin: 10px; } Listing 4-8CSS
<body> <h1>No Padding or Margin</h1> <div class="container"> <div> <p>Content</p> </div> <div> <p>Content</p> </div> </div> <h1>With Padding</h1> <div class="container has-padding"> <div> <p>Content</p> </div> <div> <p>Content</p> </div> </div> <h1>With Margin</h1> <div class="container has-margin"> <div> <p>Content</p> </div> <div> <p>Content</p> </div> </div> </body> Listing 4-7HTML
盒子大小不是继承的。它将需要被应用到所有需要被改变的元素。
显示
边距和填充允许操作元素的显示;display 属性通过指定框用于元素的呈现类型来控制元素如何相互显示。
自 CSS 出现以来,display 属性就一直是 Web 布局的基石。表 4-1 显示了每个显示值添加到规格的时间。
表 4-1
按 CSS 版本显示属性值 3
|
一级
|
第二级
(修订版 1)
|
三级
|
| — | — | — |
| One thousand nine hundred and ninety-six | Two thousand and eleven | Two thousand and eighteen |
| 街区在一条直线上的列表项目没有人 | 内嵌块桌子内嵌表格表格-行-组表格标题组表格-页脚-组表格-行表格-列-组表格列表格单元格表格标题继承 | 内容流根试车内嵌列表项目 flex 嵌入式-flex 网格嵌入式网格红宝石 |
*人选推荐
工作草案
在一条直线上的
考虑到流动内容,在流动布局中,内联元素与文本内联放置。默认情况下,以下元素是内联的:
<a>, <abbr>, <acronym>, <audio> (if it has visible controls), <b>, <bdi>, <bdo>, <big>, <br>, <button>, <canvas>, <cite>, <code>, <command>∗∗, <data>, <datalist>, <del>, <dfn>, <em>, <embed>, <i>, <iframe>, <img>, <input>, <ins>, <kbd>, <keygen>*, <label>, <map>, <mark>, <meter>, <noscript>, <object>, <output>, <picture>, <progress>, <q>, <ruby>, <s>, <samp>, <script>, <select>, <slot>, <small>, <span>, <strong>, <sub>, <sup>, <svg>, <template>, <textarea>, <time>, <u>, <tt>, <var>, <video>, <wbr>4
- 已弃用
废弃
当内容以内联方式显示时,默认情况下,元素从左到右排列,并在宽度允许的情况下并排放置。默认情况下,无论填充和边距如何,元素都将自己与文本基线对齐。如果宽度不允许,内容将在下面换行,如图 4-12 和 4-13 以及清单 4-9 和 4-10 所示。
图 4-12
内嵌元素
图 4-13
使用边框的元素
body { font-size: 24px; padding: 36px; margin: 0; } a { padding: 10px; outline: dotted 2px grey; } code { margin: 10px; outline: dotted 2px grey; outline-offset: 8px; } Listing 4-10CSS
<body> I am some text. <span>I am a span.</span> More text goes here. <code>I am code.</code> And some more text. <a href="">I am an anchor tag.</a> </body> Listing 4-9HTML
块状元素
也被认为是流动内容,块元素堆叠在另一个之上,除非它们受到另一个属性(如 float)的影响。
以下元素默认为块级元素: 5
<address>, <article>, <aside>, <blockquote>, <details>, <dialog>, <dd>, <div>, <dl>, <dt>, <fieldset>, <figcaption>, <figure>, <footer>, <form>, <h1>, <h2>, <h3>, <h4>, <h5>, <h6>, <header>, <hgroup>, <hr>, <li>, <main>, <nav>, <ol>, <p>, <pre>, <section>, <table>, <ul>
图 4-14 和 4-15 显示了块级元素的默认行为。
图 4-14
默认块级行为图
默认情况下,块级元素将占据视口的整个宽度。如果应用了宽度,即使元素内联仍有足够的空间,block 元素仍会将其自身放置在前一个元素的下方。参见清单 4-11 和 4-12 。
图 4-15
默认块级行为
html, body {
font-size: 24px;
padding: 36px;
margin: 0;
}
div {
background: rgba(0, 0, 0, .16);
height: 50px;
margin: 20px;
outline: dotted 1px gray;
outline-offset: 19px;
text-align: center;
}
div:first-of-type {
padding: 20px;
}
div:nth-of-type(2),
div:last-of-type {
width: 200px;
}
Listing 4-12CSS
<body> <div>Content</div> <div>Content</div> <div>Content</div> </body> Listing 4-11HTML
如果一个行内元素被放置在一个块元素之后,行内元素仍然会被放置在块元素之后,如清单 4-13 、 4-14 和图 4-16 所示。
图 4-16
块和内联
html, body {
font-size: 24px;
padding: 36px;
margin: 0;
}
div {
background: rgba(0, 0, 0, .16);
height: 50px;
width: 200px;
}
Listing 4-14CSS
<body> <div>Block Content</div> <span>Inline Content</span> </body> Listing 4-13HTML
内嵌块
内联式数据块利用了数据块和内联式的概念。它的行为类似于一个块元素,但会像内联一样与周围的内容一起流动。内嵌块的一个常见用例是水平导航;参见清单 4-15 和 4-16 以及图 4-17 。
图 4-17
内嵌块
html, body {
font-size: 24px;
padding: 36px;
margin: 0;
}
ul {
margin: 0;
padding-left: 0;
background: lightgray;
}
li {
list-style-type: none;
display: inline-block;
margin: 2rem;
}
a {
padding: 1rem 2rem;
background: white;
border-radius: 2rem;
}
Listing 4-16Inline-Block CSS
<body> <nav> <ul> <li><a href="">Home</a></li> <li><a href="">About</a></li> <li><a href="">Contact</a></li> </ul> </nav> </body> Listing 4-15Inline-Block HTML
内联、块和内联块元素的默认行为不足以创建经常需要的布局。在 grid 和 flex 等选项于 2018 年推出或 float(直到 1996 年推出)之前, 6 即使数据不是表格,我们也依赖于表格。长期以来,这是创建一些更复杂布局的唯一选择,因为即使 CSS 规范描述了更好的方法,浏览器也不一定支持它们。今天,这种情况不再存在,并且现在完全不赞成将表格用于显示目的,因为它阻止了辅助技术将正在显示的内容正确地传达给用户。一些例外,如电子邮件模板,仍然存在。类似于为什么历史上它们被用于网站的显示目的,大多数电子邮件客户端很少或根本不支持 CSS 布局属性,但是可访问性问题仍然存在,因此应该尽可能避免用于布局的表格。然而,对于一般的 web 使用,使用表格进行布局被认为是不好的形式并且不可访问。我们将讨论三种常用的内容布局模式:浮动、flexbox 和网格。
浮动
与 flex 和 grid 不同,float 不是 display 属性的一部分,而是一个独立的属性。
浮动的一个很好的用例是当一个图形包含在文本中时,允许文本围绕图形流动,如图 4-18 所示。
图 4-18
浮动图像
然而,使用 float 创建布局要困难得多。让我们看一个使用以下 HTML 的例子(清单 4-17 )。
<body> <h1>Flexbox</h1> <nav> <ul> <li>Nav Element</li> <li>Nav Element</li> <li>Nav Element</li> </ul> </nav> <div class="container"> <section> <div><span>1</span></div> <div><span>2</span></div> <div><span>3</span></div> </section> <main> <h2>Main Content</h2> <p>Lorem ipsum dolor sit amet, consectetur... </p> <img src="./image.png" alt=""> <p>Varius morbi enim nunc faucibus a. Ut placerat... </p> <p>Eget duis at tellus at urna condimentum mattis... </p> </main> <aside> <h2>Aside</h2> <p>Placerat duis ultricies lacus sed turpis tincidunt id aliquet... </p> </aside> </div> </body> Listing 4-17Example HTML
我们将尝试实现图 4-19 中的布局。
图 4-19
布局示例
从导航开始,我们已经看到了图 4-20 (列表 4-18 和 4-19 )中的问题。
html, body {
padding: 36px;
margin: 0;
}
h1 {
text-align: center;
}
nav {
border-top: solid 1px gray;
border-bottom: solid 1px gray;
}
nav ul { padding: 0; }
nav li {
list-style-type: none;
width: 33%;
float: left;
}
Listing 4-18Float: CSS
一旦列表项被浮动,边框就会在它们上面升起,左边栏中的数字会自动出现在它的左边(图 4-20 )。
图 4-20
浮动导航
为了让数字正常工作,必须清除浮点。我们可以使用列表中的display: flow-root
来清除浮动。这是一处相当新的房产。历史上,“clearfix”或“group”类(见清单 4-19 )会被添加到列表中。
.clearfix::after { content: ""; clear: both; display: table; } Listing 4-19Clearfix
继续向下页面,我们可以接近带有浮动的目标布局(图 4-19 )(见清单 4-20 和图 4-21);但是,该列并没有在屏幕底部对齐。当调整窗口大小和内容重排时,将左栏中的数字居中以看起来均匀分布也不起作用。此外,由于填充用于使数字居中,如果内容是为完整的句子而不是数字编辑的,则必须重新计算填充,否则文本将不再居中。
图 4-21
尝试使用浮动的布局
html, body {
padding: 36px;
margin: 0;
}
h1 {
text-align: center;
}
nav {
border-top: solid 1px gray;
border-bottom: solid 1px gray;
display: flow-root;
}
nav ul {
padding: 0;
margin: 0;
}
nav li {
box-sizing: border-box;
list-style-type: none;
float: left;
padding: 1rem;
text-align: center;
width: 33.33%;
}
section {
float: left;
background: rgba(0, 0, 0, .16);
}
section div {
box-sizing: border-box;
color: white;
background: rgba(0, 0, 0, .50);
height: 100px;
width: 100px;
text-align: center;
padding: 37px;
margin-top: 5rem;
margin-bottom: 5rem;
}
main {
background: rgba(0, 0, 0, .05);
box-sizing: border-box;
padding: 30px;
float: left;
width: calc(100vw - 244px - 30%)
}
aside {
box-sizing: border-box;
background: rgba(0, 0, 0, .16);
width: 30%;
padding: 30px;
float: right;
}
img {
width: 100%;
max-width: 250px;
float: left;
padding-right: 20px;
}
Listing 4-20CSS for Figure 4-21
让图片周围的文本工作得非常好,因为这就是 float 的设计目的。然而,布局的其余部分有一些问题。填充以及设置的高度和宽度的组合很可能会使较长的内容在最左边的栏中扩展到其容器之外。同样令人担忧的是,100%不容易被 3 整除,因此根据浏览器决定如何计算每个导航元素的宽度,内容可能会以不应该的方式被推来推去。最后,柱子上的背景并不一致。
出于所有这些原因,这将是一个容易出错且难以维护的布局。为了实现这种布局,并使 UI 流畅,在引入 flexbox 和 grid 之前,就需要使用表格、CSS display: table
属性和/或 JavaScript。
flex box(flex box)的缩写形式
从历史上看,在前面的例子中,有两种类型的布局非常困难,包括:
- 不管其中的内容如何,背景颜色都要对齐的多列
- 垂直居中内容
今天我们有了 flexbox。解决这两个问题是 flex 真正的亮点。Flexbox 还允许根据内容量动态确定列宽。当创建一个需要控制容器中元素间距的布局时,使用display: flex
特别有用。
让我们再次尝试创建我们的布局,这次使用 flexbox(清单 4-21 )。
h1 { text-align: center; } nav { border-top: solid 1px gray; border-bottom: solid 1px gray; } ul { display: flex; padding-left: 0; justify-content: space-around; } .container { display: flex; } aside { background: rgba(0, 0, 0, .16); flex-basis: 30%; flex-shrink: 0; padding: 30px; } section { background: rgba(0, 0, 0, .5); align-items: center; display: flex; flex-direction: column; justify-content: space-evenly; } section div { color: white; background: rgba(0, 0, 0, .50); align-items: center; display: flex; height: 100px; justify-content: center; width: 100px; } main { background: rgba(0, 0, 0, .05); padding: 30px; } li { list-style-type: none; } img { width: 100%; max-width: 250px; float: left; padding-right: 20px; } Listing 4-21Flexbox CSS
剖析前面的布局,我们将 flex 应用于布局的三个区域,三个内容列、导航和最左边的列本身。对于最左边的列和导航,使用 flex 来分别垂直和水平地分布容器中的内容。
弯曲方向
Flexbox 应用于两个轴:主轴和横轴。主轴定义了布局的方向。属性flex-directio
n 用在容器上,包括四个选项:row
、row-reverse
、column
、column-reverse
,默认为row
。这将决定元素显示的顺序和布局的方向(见图 4-22 )。
图 4-22
柔性盒主轴
默认情况下,Flexbox 会尝试将所有内容放在一行中,但是可以使用flex-wrap
属性让内容换行。flex-wrap
属性可以取以下任意值(图 4-23 ):
图 4-23
包装
nowrap
–
这是默认的。所有项目将被放置在主轴线后的一行上,必要时会导致溢出。wrap
–
项会从上到下换行。wrap-reverse
–
项将自下而上换行。
通过添加到 row、column 或 wrap,我们可以改变元素的显示顺序。如果我们想单独移动序列中的特定元素,我们可以使用order
。属性采用整数,默认情况下为 0。如果一个元素被赋值为 1,并且所有其他元素被设置为缺省值 0,那么它将出现在最后。如果赋值为-1,该元素将出现在开头。因此,顺序基于提供的序列,然后基于使用 order 属性分配给每个元素的值进行加权。
调整内容
为了确定每个元素在主轴上的位置,我们在容器上使用justify-content
。可能的值如下:
flex-start –
元素放在容器的开头。弹性启动是默认值。
flex-end –
元素被放置在容器的末端。
居中 –
元素被放置在容器的中心。
space-between –
元素在容器中均匀分布,容器的边缘与第一个和最后一个项目之间没有空间。
空间环绕 –
元素在容器中均匀分布,容器边缘与第一个和最后一个项目之间的空间是其他项目之间空间的一半。
空间均匀 –
元素在容器中均匀分布,边缘与第一个和最后一个元素之间以及元素之间的间距相同。
Inline-Block
Flex-start、middle 和 flex-end 比 inline-block 有一些优势。对于很多用例来说,display: inline-block
配合text-align
使用可以达到同样的效果;然而,内联块在间距方面有一些复杂性。即使边距设置为 0,元素之间也会出现小间隙。因此,元素的总和等于容器宽度的 100%的布局变得很难创建。让我们看看代码及其输出(清单 4-22 和 4-23 以及图 4-24 )。
例 1
图 4-24
内嵌块间隙
html, body {
padding: 12px 36px;
margin: 0;
font-size: 32px;
}
ul {
padding-left: 0;
}
li {
display: inline-block;
padding: 10px 20px;
background: grey;
color: white;
}
Listing 4-23Inline-Block CSS
<body> <h1>Example 1</h1> <nav> <ul> <li>inline-block element</li> <li>inline-block element</li> <li>inline-block element</li> </ul> </nav> </body> Listing 4-22Inline-Block HTML
请注意内联块元素之间的间隙。列表元素上没有边距。弯曲的项目不会受到这种意外行为的影响。
对齐项目和对齐自身
横轴垂直于主轴。回顾我们最初的 flexbox 示例(图 4-24 ,我们有三列内容,左边的数字部分、中间的内容部分和右边的部分。对于它们中的每一个都有相同的长度,由它们各自的背景颜色在底部对齐来表示,我们可以使用align-content
属性。这些值如下所示:
拉伸 –
元素将扩展到容器的可用高度(如果伸缩方向为列,则为宽度)。
对于列来说,这种行为允许弯曲的元素增加大小,类似于表格行,以便包含的所有元素都与数组中最新的元素具有相同的高度。
flex-start 或 start –
元素将与容器顶部对齐,类似于vertical-align: top
。
flex-end 或 end –
元素将与容器底部对齐,类似于vertical-align: bottom
。
中心 –
元素将对齐容器的中间,类似于vertical-align: middle
。
基线 –
元素将与文本基线对齐,类似于vertical-align: baseline
。
上述属性是在容器上设置的,将应用于中的所有元素。要操作单个元素并使其行为不同于其他元素,我们可以使用align-self
。其值与前面列出的align-items
的值相同。
弹性基础、弹性增长和弹性收缩
Flex-basis 允许设置元素开始的基本宽度。它们的内容将决定它们是否需要增长或收缩以适应容器中的可用空间。
为了确保内容填满容器中 100%的可用空间,可以应用一个 flex-grow 属性。默认设置为 0,它指定弯曲项目的增长因子。该值基于比率。如果容器的所有同级都具有相同的值,那么它们都将增长相同的量,因此元素的总和等于可用宽度的 100%(如果 flex-direction 设置为 column,则为高度)。否则,它将根据每个弯曲元素上定义的比率进行分配。
Flex-shrink 的工作方式与 flex-grow 类似,只是收缩内容以防止溢出。默认情况下设置为 1,它可以设置为 0,并与 flex-basis 结合使用,以确保弯曲元素具有固定的宽度(或高度,如果将 flex-direction 设置为 column)。
由于能够动态处理所提供的空间,display-flex 使得生成流畅的设计和对齐内容比以往任何时候都更容易,而无需为了显示而使用表格,但它是非常单向的。然而,网格带来了第二维度。
格子
最近引入的还有网格。grid 的亮点在于它让开发人员能够命名各个部分,使代码易于阅读和维护。与 flexbox 一次只处理一个方向不同,grid 允许定义行和列。这些部分可以是名称,如清单 4-24 中所示,或者基于行号和列号,如清单 4-26 中所示。
.container { display: grid; grid-template-columns: 1fr 1fr 1fr 300px; grid-template-rows: 46px auto 36px; grid-template-areas: "header header header header" "main main . sidebar" "footer footer footer sidebar"; } Listing 4-24Grid CSS
Grid-template-columns
定义了四列。前三个等宽,最后一个 300 像素。这里使用的“fr”单位以比率的形式表示剩余空间的分数。 7 最后一列将被赋予 300 像素的宽度;其他三个将获得与其宽度相等的剩余空间。
Grid-template-rows
定义三行,第一行的高度和最后一行的高度分别为 46 和 36 像素。中间一行设置为 auto,将调整其高度以适应其内容。
Grid-template-areas
在刚刚创建的 4 乘 3 的网格上,按行定义命名区域,如图 4-25 所示。
图 4-25
网格模板区域
所以看看完整的实现,代码看起来和输出看起来如下(清单 4-25 和 4-26 )。图 4-26 显示输出。
html, body {
padding: 36px;
margin: 0;
}
header {
grid-area: header;
background: rgba(0, 0, 0, .1);
text-align: center;
padding: 5px;
}
main {
grid-area: main;
background: rgba(0, 0, 0, .2);
padding: 10px;
}
.sidebar {
grid-area: sidebar;
padding: 10px;
background: rgba(0, 0, 0, .3);
}
footer {
grid-area: footer;
background: rgba(0, 0, 0, .5);
text-align: center;
color: white;
}
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 46px auto 36px;
grid-template-areas:
"header header header header"
"main main . sidebar"
"footer footer footer sidebar";
}
header, footer {
display: flex;
align-items: center;
justify-content: center;
}
header h2, footer h2 {
margin: 0;
}
Listing 4-26Grid CSS
<body> <div class="container"> <header> <h2>Header</h2> </header> <main> <h2>Main</h2> <p>Lorem ipsum dolor sit amet, consectetur… </p> <p>Quisque faucibus, augue sed varius ornare… </p> </main> <aside class="sidebar"> <h2>Sidebar</h2> <ol> <li>Lorem</li> <li>Ipsum</li> <li>Dolor</li> <li>Sit</li> <li>Amet</li> </ol> </aside> <footer> <h2>Footer</h2> </footer> </div> </body> Listing 4-25Grid HTML
请注意“.”第二行的grid-template-areas
为容器类;这允许三列/第二行网格部分保持空白。
图 4-26
电网输出
每个 UI 元素都被设置为其命名的grid-area
。这样做的好处是命名可以遵循被定位容器的目的,使得代码易于阅读和维护。此外,当为了响应性而重新定位元素时,唯一需要更新的属性是grid-template-areas
。
Grid 还允许使用列号和行号来定义区域。如图 4-27 所示,列的编号从最左边的 1 开始,行的编号从最上面的 1 开始。
图 4-27
网格行和列
使用与前面相同的 HTML,我们可以通过给每个部分分配grid-row
和grid-column
值来获得相同的输出(清单 4-27 )。
html, body {
padding: 36px;
margin: 0;
}
header {
grid-area: header;
background: rgba(0, 0, 0, .1);
text-align: center;
padding: 5px;
grid-row: 1;
grid-column: 1 / 5;
}
main {
grid-area: main;
background: rgba(0, 0, 0, .2);
padding: 10px;
grid-row: 2;
grid-column: 1;
}
.sidebar {
grid-area: sidebar;
padding: 10px;
background: rgba(0, 0, 0, .3);
grid-row: 2 / 4;
grid-column: 4;
}
footer {
grid-area: footer;
background: rgba(0, 0, 0, .5);
text-align: center;
color: white;
grid-row: 3;
grid-column: 1 / 4;
}
.container {
display: grid;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-template-rows: 46px auto 36px;
}
header, footer {
display: flex;
align-items: center;
justify-content: center;
}
header h2, footer h2 {
margin: 0;
}
Listing 4-27Grid CSS
Grid-row
和grid-column
可以用一个整数(grid-row: 1
)或者用一个/
( grid-row: 1/3
)分开的两个整数来定义。当只使用一个整数时,该部分将从指定的行开始,跨越一列或一行,例如前面示例中的main
元素。当使用由/
分隔的两个整数时,该部分将从第一个整数指定的行开始,到第二个整数指定的行结束,如页脚的grid-column
值所示。
与 flexbox 或 table 类似,可以调整内容在节中的位置。可以在 grid 容器上使用 justify-items 属性来确定节或单元格内的内容如何从左到右对齐。其值为开始、结束、中心和拉伸,如图 4-28 所示。拉伸是默认值。
图 4-28
对齐-项目值
通过使用特定部分的justify-self
属性,可以使用相同的值来更改特定单元格的对齐方式。
要指定内容如何在单元格中垂直对齐,我们可以将 align-items 属性分配给 grid 容器。其值与之前的相同,也默认为拉伸(图 4-29 )。
图 4-29
使用 Align-Items 进行垂直对齐
与justify-self
类似,相同的值可以与单个部分上的align-self
属性一起使用,以允许单元格的行为不同于容器上的默认设置。
就像 flexbox 一样,grid 也有一个 justify-content 和 align-items 属性。它们与 flexbox 具有相同的价值观和工作方式。它们在容器内水平和垂直定位网格单元。当网格本身小于网格容器时,这非常有用。
为了增加单元格之间的空间,可以使用网格间隙。这将决定每行和/或每列之间的间距。分别使用网格-行-间隙和网格-列-间隙来设置它们。例如,grid-gap: 5px 2rem
将在每行之间设置 5 个像素的间隙,在每列之间设置 2 个 rems 的间隙。
最后,grid 有一个自动布局算法。当要放置的项目多于 CSS 定义的项目时,或者当一个元素的 grid-column 或 grid-row 值超出容器模板中定义的界限时,就会出现这种情况。使用网格自动流动属性控制自动放置的行为。自动流动可以优化灌装
- 用行值逐行
–
填充行,并根据需要添加新行 - 按列,使用列值
–
填充列并根据需要添加新列
为了让网格填充可能存在的任何间隙,可以将 dense 添加到行和列值中,grid-auto-flow: column dense
。
网格现在在 evergreen 浏览器中得到更广泛的支持,但在 Internet Explorer 11 等较老的浏览器中存在一些兼容性问题,Internet Explorer 11 目前还没有完全实现该规范。
使用 Flexbox 或 Grid 时的可访问性
Grid 和 flexbox 提供了随意重新定位和重新排序内容的能力,只需改变一两个属性,而不考虑 HTML 中的顺序。这对于可访问性来说是个问题。
网站内容可访问性指南指出
当内容呈现的顺序影响其含义时,可以通过编程来确定正确的阅读顺序。(A 级)准则 1.3.2 有意义的序列(A 级) 8
当使用 CSS 改变元素的顺序时,确保编程顺序仍然有意义是很重要的。
响应性设计
响应性设计实现的核心是媒体查询。
Media Query
媒体查询允许作者测试和查询用户代理或显示设备的值或特征,而与正在呈现的文档无关。它们用在 CSS @media 规则中,以有条件地将样式应用于文档,以及各种其他上下文和语言,如 HTML 和 JavaScript。
—媒体查询级别 4 9
更具体地说,使用媒体查询允许您根据视口的特性有条件地更改样式。响应式设计中经常使用的是与视口宽度相关的媒体查询,最终目标是为小型移动屏幕和大型桌面显示器以及介于两者之间的任何设备定制布局。为了实现这一技术,在样式将改变的不同视口宽度处选择断点。CSS 可能如清单 4-28 所示。
@media (min-width: 500px) { ... } Listing 4-28Media Query
其中除非视口的宽度大于 500 像素,否则括号之间的任何内容都不会被应用。宽度范围也可以这样声明(清单 4-29 )。
@media (500px <= width <= 700px) { ... } Listing 4-29Ranged Media Query
当视口宽度在 500 和 700 像素之间时应用样式的位置。
人们很容易陷入这样的思维陷阱:需要为每个断点重写样式。此外,当从窄到宽布局时,需要覆盖的样式比从宽到窄少得多。这是因为在较窄的布局中,项目比宽布局更容易简单地堆叠。对于大多数用例,设置响应以减少代码量的最简单方法是从窄布局开始,然后随着屏幕变宽而增加。我们来看看这个样本设计的实现(图 4-30 )。
图 4-30
响应式设计
为了实现这种布局,将使用清单 4-30 中的 HTML。
<body> <header> <h1>Responsive Design</h1> </header> <nav> <ul> <li><a href="">Link 1</a></li> <li><a href="">Link 2</a></li> <li><a href="">Link 3</a></li> </ul> </nav> <h2>My Items</h2> <main> <article> <h3>Article Title</h3> <p>Lorem ipsum dolor sit amet, consectetur elit...</p> <a href="">Read More</a> </article> ... </main> </body> Listing 4-30Responsive Layout HTML
我们做的第一件事是建立一些不管屏幕大小都适用的基本样式(清单 4-31 )。
html, body {
margin: 0;
padding: 0;
}
body {
box-sizing: border-box;
font-family: 'Gill Sans', 'Gill Sans MT', ... sans-serif;
height: 100vh;
left: 0;
margin: 0;
position: absolute;
top: 0;
width: 100vw;
}
h1, h2, h3 {
font-family: Impact, Haettenschweiler, ... sans-serif;
margin: 0;
}
header {
background: rgba(0, 0, 0, .1);
box-sizing: border-box;
grid-area: header;
text-align: center;
padding: .75rem;
}
nav {
background: #c6c6c6;
grid-area: nav;
}
nav ul {
margin: 0;
padding: 0;
display: flex;
justify-content: space-evenly;
}
nav li { list-style-type: none; }
nav a {
display: block;
padding: 1rem;
}
h2 {
background: #c6c6c6;
grid-area: title;
padding: .5rem 1rem;
}
main {
padding: 1rem;
grid-area: main;
overflow: auto;
}
article {
background: #e9e9e9;
border-left: solid 2.5rem gray;
padding: 1rem;
margin-bottom: 1rem;
}
article a {
display: block;
text-align: right;
}
Listing 4-31Base Styles
然后我们可以为每个断点添加布局特定的信息(清单 4-32 和 4-33 )。
/* Desktop layout */ @media (min-width: 500px) { body { grid-template-columns: 1fr; grid-template-rows: auto auto auto; grid-template-areas: "header" "nav" "title" "main"; height: auto; overflow: auto; } main { column-width: 250px ; } article { break-inside: avoid; } h2 { background: none; } } Listing 4-33Desktop CSS
/* Mobile layout */ body { display: grid; grid-template-columns: 1fr; grid-template-rows: 4rem auto auto 3rem; grid-template-areas: "header" "title" "main" "nav"; height: 100vh; overflow: hidden; } Listing 4-32Mobile CSS
注意,在清单 4-33 中,为桌面用户重新调整布局只需要很少的 CSS。这是因为基本样式已经应用,不需要复制。在这里,我们还可以看到命名区域对于网格布局的优势,以及组织它们以实现正确布局的便利性。
摘要
元素受制于盒子模型,盒子模型规定了元素的宽度、填充、边距和边框的行为方式。当这些元素放在一起时,就形成了一个布局。有多少种布局就有多少种方法,但是每种技术都有自己的优点和缺点。我们已经研究了 float、flexbox 和 grid,以及针对响应性布局的媒体查询。
在下一章,我们将会看到 CSS 没有像预期的那样工作的场景,特别关注浏览器之间的差异。
掌握保证金崩溃。(2019 年 8 月 4 日)。检索自 https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Mastering_margin_collapsing
2
8 箱型。(2019 年 8 月 4 日)。检索自 www.w3.org/TR/CSS22/box.html
3
显示。(2019 年 8 月 4 日)。检索自 https://developer.mozilla.org/en-US/docs/Web/CSS/display
4
内嵌元素。(2019 年 8 月 4 日)。从 developer 中检索。mozilla。org/en-US/docs/Web/HTML/Inline _ elements
5
块级元素。(2019 年 8 月 4 日)。检索自 https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
6
级联样式表,级别 1。(1996 年 12 月 17 日)。检索自 www.w3.org/TR/CSS1/#float
7
CSS 网格布局模块级别 1: 7.2.3。灵活的长度:“fr”单位。(2019 年 8 月 5 日)。检索自 www.w3.org/TR/css3-grid-layout/#fr-unit
8
网页内容可访问性指南(WCAG) 2.0,有意义的序列。(2019 年 8 月 7 日)。检索自 www.w3.org/TR/2008/REC-WCAG20-/#content-structure-separation-sequence
9
媒体查询级别 4。(2019 年 8 月 9 日)。检索自 www.w3.org/TR/mediaqueries-4/
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/160153.html