CSS3 布局技巧高级教程(一)
一、克服网页布局的挑战
Electronic supplementary material The online version of this chapter (doi:10.1007/978-1-4302-6503-0_1) contains supplementary material, which is available to authorized users.
欢迎使用 Pro CSS3 布局技术!
虽然说风格比内容更重要是不公平的,但我们都希望网站看起来不错,对我们的设备做出反应,并在参与度上与其他媒体竞争,这确实是事实。这意味着,作为网页设计者,我们被寄予了很大的期望,去生成更加直观的布局、用户友好的展示和与设备无关的代码。虽然我们已经准备好迎接挑战,但是我们所拥有的一些核心工具要么从来就不是为版面设计设计的,要么已经用了十多年了。网络在发展,但是布局工具却没有——直到 CSS3 的出现和实现。
这本书将涵盖以下主题:
- 利用 CSS3 布局模块
- 决定今天什么是可行的
- 发现即将在真实环境中使用的内容
- 在 CSS3 不可用的情况下,学习使用 CSS Level 2.1 规范进行布局的最佳实践方法
在我们对新玩具过于兴奋之前,首先很有必要理解为什么我们会处于这种境地。为了正确理解,我们需要回顾 HTML(超文本标记语言)和 CSS(层叠样式表)的发展。不要担心:这篇评论不会是一次详尽的记忆之旅,但是当您下一次使用 CSS 来设计布局时,它会很好地为您服务。
HTML 属性和标签
当网络发展到非常早期的时候,HTML 被用于标记和样式。HTML 属性和标签定义了页面的外观。关于如何标记内容的决定很可能来自标签的默认视觉特征,也可能来自任何语义层次的感觉。考虑以下示例及其用途:
- 标签用于大而粗的文本,同时也表示页面上最重要的信息。
<p>对于小文本,还可以显示段落文本,即标题的骨架。<center>标签来对齐段落和表格。最初,它们只是用来显示表格数据的。这些标记开始被用于创建复杂的多栏布局,这对于语言中有限的标记和格式属性来说是不可能的。
尽管这种方法非常足智多谋且富有创造性,但它导致了一些既难以阅读又难以维护的可怕代码。随着布局需求变得越来越复杂,表格开始相互嵌套,通常有几层之深。视觉上多余的透明间隔 gif 越来越多地用于确保元素的正确定位。一个简单的页面包含成百上千行难以理解的代码变得很常见,如清单 1-1 所示。
<FONT FACE=TIMES COLOR=#FF0000 SIZE=3>
<H2><I>WELCOME TO MY WEBSITE</I></H2>
<CENTER>
<TABLE WIDTH=720 HEIGHT=480 BORDER=2 BGCOLOR=BLACK>
<TR>
<TD BACKGROUND=texture.gif>
<TABLE WIDTH=360 HEIGHT=480 BORDER=0>
<TR>
<TD>
<IMG SRC=spacer.gif WIDTH=360 HEIGHT=10><BR>
<H1>ABOUT MY SITE</H1>
<P>...</P>
</TD>
</TR>
</TABLE>
</TD>
<TD BACKGROUND=texture2.gif>
<TABLE WIDTH=360 HEIGHT=480 BORDER=0>
<TR>
<TD>
<IMG SRC=spacer.gif WIDTH=360 HEIGHT=10><BR>
<H1>ABOUT ME</H1>
<P>...</P>
</TD>
</TR>
</TABLE>
</TD>
</TR>
</TABLE>
</CENTER>
</FONT>
Listing 1-1.Example of a Vast Listing to Create a Basic
Two-Column Layout
这种编码和维护网站的方法提出了一些问题,这些问题对于使用 HTML 的每个人来说都是显而易见的。Web 需要一种将内容和样式分开的方法,因此万维网联盟(W3C)承担了这项任务,开发了 CSS Level 1 规范。
CSS 的到来
随着 Web 开始超越简单的基于文本的媒体,成为更加视觉化、杂志式的体验,很快就发现 HTML 样式带来的限制将成为一个问题。负责定义网络标准的 W3C 认为将风格与内容分开更明智,并开始研究一种新的语言,这种语言将控制内容的美学处理,而不考虑内容的语义。解决方案是层叠样式表(CSS),它是现代网页上所有设计和布局的基础。
CSS 级别 1
CSS Level 1 (CSS1)并不打算作为实现布局的解决方案。相反,这个想法是替换所有基本的视觉特征,比如颜色、字体、边距和边框,这些都是用 HTML 标签和属性指定的。结果是以下有限的属性集:
- 块级元素的宽度和高度
- 漂浮,清除漂浮元素
- 边距和填充
- 背景颜色和图像
- 边界
- 字体和字体样式
- 列表样式
- 一些基本的排列
CSS1 允许设计采用 HTML 代码本身之外的单个元素的基本样式。第一次,一个网站的外观可以通过一个外部文件来控制。这对页面设计的维护和一致性有很大的好处,因为一行 CSS 代码现在可以影响整个网站。以前,简单的标题颜色改变意味着单独编辑网站中的每个页面。
由于浏览器市场缺乏竞争以及设计者的需求相对缓慢,网络浏览器花了一些时间来采用新的 CSS 规范。直到 20 世纪 90 年代末,设计人员才能够依靠常用的浏览器,如 Netscape Navigator 和 Microsoft Internet Explorer,以近乎精确的方式解析和呈现 CSS 代码。巧合的是,微软和网景正在争夺霸权。如图 1-1 所示,Internet Explorer 是最终的胜利者。
图 1-1。
Internet Explorer on the Mac was the first CSS-compliant browser
也许你熟悉使用浮动元素作为创建布局的系统。然而,CSS1 中引入的float和clear属性从来不是为了这个目的。它们旨在提供以前属于 HTML 的ALIGN=LEFT和ALIGN=RIGHT属性的 CSS 传真。计划是在图像上使用它们,让文本自动围绕元素流动。第二章讨论了如何有效地使用浮动元素。
您可能已经注意到了规范中的宽度和高度属性,它们可以用来定义元素的尺寸。但是它们的目的仅仅是复制以前在 HTML 中作为图像、表格和其他块级元素的属性使用的相同属性。
CSS1 显然是网络的一个里程碑。随着它在 20 世纪 90 年代后期被浏览器实现,W3C 开始致力于级联样式表语言的下一个迭代:CSS Level 2。
CSS 级别 2
CSS Level 2 设计的一个目的是在样式表中实现一些布局控制。将布局从 HTML 转移到 CSS 的第一次尝试是基于网页设计者已经习惯的表格模型。引入的一个关键的新属性是display,规范的一部分提供了要应用的值table-cell。(见图 1-2 。)该值在正确呈现后,可以让您创建一个类似于使用表格的布局,并包含等高的列。
图 1-2。
CSS2 introduced the display : table-cell property to mimic the existing tabular layout solution being used by designers
可悲的是,并不是每个浏览器都支持 CSS2 标准,所以它实际上在网络上是不可用的。虽然很多浏览器支持display: table-cell,但是 Internet Explorer 6 不支持。因此,可靠的布局仍然牢牢地留在 HTML 表格的领域中。此外,浏览器供应商对松散编写的 CSS2 规范的解释略有不同。因此,浏览器呈现的同一页面不一致,让设计者和消费者都很沮丧。
W3C 已经开始着手 CSS 的下一个版本——level 3。然而,认识到 CSS2 规范中的缺陷,该组织搁置了 CSS3,转而花了很长时间致力于解决 CSS2 产生的互操作性问题,最终开发了 CSS Level 2.1 形式的后继版本。
因为 Internet Explorer 实际上享有垄断地位,所以大多数网站的设计都考虑到了它的呈现方式。最终,设计师采用了风格和内容的分离,但他们被迫使用浮动和绝对定位来设计布局。正如您已经看到的,这些属性从来就不是用来作为布局工具的。但是足智多谋的网页设计社区找到了劫持它们的方法,并且在不使用表格的情况下生成巧妙的布局。
浏览器
图 1-3 显示了权力的天平向谷歌的 Chrome 浏览器倾斜。然而,很大一部分用户依赖于 Internet Explorer。
图 1-3。
Market share statistics from 2013 show browser usage. Image from StatCounter.com
让我们来看看下面的例子:
- 火狐浏览器
- 微软公司出品的 web 浏览器
- 铬
火狐浏览器
1999 年美国在线(AOL)收购网景浏览器后,源代码被开源,允许开发者社区为其做出贡献。这一令人钦佩的行动导致了一种全新的浏览器功能的形成。其结果是 Mozilla 基金会和 2004 年新的网络浏览器 Firefox 的出现。
Firefox 单枪匹马引发了一场新的浏览器大战。微软仍然是占主导地位的浏览器,市场份额在 90%左右,但 Firefox 慢慢削弱了它的主导地位。这最终迫使微软在中断六年后更新了老化的 ie 浏览器。
微软公司出品的 web 浏览器
Internet Explorer 7 于 2007 年发布。它给用户带来了选项卡式浏览,但在标准采用或对设计者 CSS 的额外支持方面很少。
2000 年代末,Internet Explorer 8 引入了对display: table-cell的支持,最终实现了一定程度的互操作性。令人沮丧的是,仍有超过一半的网络用户使用 Internet Explorer 6 的问题。
Note
即使在今天,Internet Explorer 6 也只有大约 1%的市场份额。大多数网页设计者现在选择忽略这部分市场,因为提供支持所花费的时间与回报的递减不成比例。
铬
当微软继续拖后腿的时候,Firefox 开始流行,一个新的竞争者被引入市场。搜索引擎巨头谷歌基于开源 WebKit 项目开发了自己的品牌浏览器,并于 2008 年底发布。Chrome(见图 1-4 )很快取代 Firefox 成为网络上第二受欢迎的浏览器,并朝着完全符合 CSS2.1 迈出了一大步。
图 1-4。
Google’s Chrome is now the world’s most widely used browser
CSS 盒子模型
在 2000 年至 2009 年期间,网页设计师不得不应对非常头痛的问题,即差异巨大的渲染引擎以完全不同的方式显示他们的代码。最大的问题是 CSS box-model(见图 1-5 ),它用于从指定的宽度、高度、边距和填充的组合中确定元素的宽度。由于编写松散的 CSS2 规范,微软对 box 模型的解释与其他浏览器供应商社区不同,导致了竞争浏览器之间文档呈现的不一致;在 Internet Explorer 6 以外的浏览器中,页面无法按预期呈现。因此,采用了变通方法、shivs 和 hacks 来创建布局,这些布局基于符合 CSS2.1 规范的代码,但也适用于不符合标准的 IE6 渲染引擎。
图 1-5。
An illustration of the different box-model rendering across browsers
微软目前正在邀请用户放弃 Internet Explorer 6,并认为它只不过是一个糟糕的记忆。然而,由于 Internet Explorer 7 和 8 的兼容模式,很大一部分浏览器市场份额仍然使用 Internet Explorer 6 渲染方法。因此,常见到针对微软浏览器的专家样式表。此外,整个 JavaScript 库都包含在一个页面中,以便符合 polyfill 的布局呈现。
为了应对不同浏览器之间实现互操作性的困难,开发了一系列测试来帮助衡量不同浏览器相对于 CSS 规范的性能。这些酸性测试被标准支持者用来向包括微软在内的浏览器供应商施压,要求他们改进软件。
最后,在 21 世纪中后期,微软开始开发符合标准的 IE 浏览器版本:IE8 于 2008 年发布,IE9 于 2011 年发布。与此同时,随着 iPhone 等设备的发布,移动领域正在发生一场革命。这些新的支持互联网的智能手机支持新开发的 CSS3 规范的元素,以及 HTML5,并进一步增加了现有市场领导者采用标准的压力。
CSS 级别 3
CSS Level 3 不同于之前的迭代,因为它是模块化的。这允许 CSS 工作组在规范成熟时发布不同的部分,并且浏览器供应商能够快速采用功能。CSS 级模块化方法大致分为四个部分:
- 选择器和逻辑
- 装饰效果
- 排版(包括对国际化的支持)
- 布局
最有趣的是,布局方面封装了一系列新模块,这些模块在页面布局方面提供了灵活性和标准化,这在 Web 上是前所未有的。令人沮丧的是,即使有了这个新规范,我们仍在旧浏览器和不完整实现的阴影下挣扎。
CSS 布局模块
CSS Level 3 为设计者的武库中增加了一系列新的布局工具。这些模块中的每一个都提供了一种令人兴奋的方法,只使用 CSS 来制作布局。这些模块将改变你创作风格的方式,更重要的是,改变你的 HTML 标记。
四个主要模块正在开发中,而不是一个单独的方法来制作布局。每一个都有自己的概念、规则,以及最重要的浏览器支持。以下章节深入探讨了每个模块:
- CSS 多列布局模块
- CSS 灵活盒子布局(Flexbox)模块
- CSS 网格布局模块
- CSS 区域布局模块
在第 4–7 章节深入每个模块的全部细节之前,您将在第二章中对每个模块进行适当的概述。与此同时,您可以通过快速查看每个模块在布局方面可以实现的功能来激起您的兴趣。
CSS 多列布局
CSS 多列布局是新布局模块中最容易掌握和实现的。它也是对浏览器支持最成熟的模块,尽管它可能是对高级布局最没用的模块。
多栏布局(见图 1-6 )确实如你所料:它使内容很容易自动流入多栏,这可以适应可用空间,以便较小的屏幕比较大的屏幕呈现较少的栏。第四章考查本模块。
图 1-6。
CSS Multi-column Layout rendering content into a column-based layout
CSS 灵活的盒子布局
CSS 柔性盒布局(或 Flexbox)已经经历了几次不同的修订。多亏了一些早期的浏览器实现,网络上有许多现在已经不存在的例子。这导致了网页设计社区的混乱;但是现在规范已经稳定了,对模块有了相当好的跨浏览器支持。
Flexbox 的设计并不是为网站提供一个完整的布局解决方案,但是它确实可以让你创建一些元素,比如工具栏和标签区域,这些元素可以响应用来查看它们的设备(见图 1-7 )。第五章深入探讨了 Flexbox 模块。
图 1-7。
Flexbox helps solve some common user interface design problems
CSS 网格布局
CSS 网格布局是最令人兴奋的布局模块之一,因为它第一次允许设计真正与表现分离。元素可以使用 CSS 网格布局重新排序,因此标记到达的顺序与布局无关。对于努力创建有意义的语义标记的设计师来说,这是一个极好的消息,更不用说该模块允许在定义的网格内进行布局控制,并完成垂直对齐(见图 1-8 )。第六章研究了所有的细节和浏览器支持。
图 1-8。
The CSS Grid layout brings a flexible and reliable grid layout option to the Web for perhaps the first time
CSS 区域布局
CSS 区域布局允许内容在杂志风格的布局中从一个区域到另一个区域的复杂流动。这种复杂的布局超出了其他布局模块。它可用于创建响应不同设备的动态、流畅布局(参见图 1-9 )。CSS 区域布局还不是一个完全成熟的模块,但是第七章会检查所有的细节,包括当前的浏览器支持。
图 1-9。
CSS Regions Layout offers an exciting way to position content in and around elements without affecting document flow Note
还有一些新兴的布局模块,虽然还不是 CSS 规范的一部分,但提供了更多的布局选项。在这本书的结尾你会看到一些。
正如您所料,目前使用 CSS3 模块存在许多潜在的问题:一些仍在开发中,而另一些已经相当成熟。一些浏览器几乎已经完全实现了单个模块的标准,但是其他浏览器还在继续努力合并或者在某些情况下定义它们。这可能是一个雷区,但这本书引导你安全地通过陷阱!
这本书将如何帮助你
现在你知道你面对的是什么了,让我们快速看看你能从阅读这本书中得到什么。我的目的是帮助你理解在你的网站设计方法中可以使用的关键 CSS 布局技术。
CSS3 提供了大量令人兴奋的可能性,这本书着眼于每个关键的布局模块。给你一个警告:作为阅读这本书的结果,你将改变你构建网站布局的方式!但话虽如此,你也会看到一些限制——标准本身的限制,或者人们用来访问网络的浏览器软件带来的限制。
到这本书结束时,你会对你今天能做什么有一个现实的看法,以及对未来一两年可能发生的事情有一个好的想法。您还将看到一系列有用的多填充技术示例,它们可以为您的布局提供一个优雅的后备。
当然,如果忽略了大部分继续使用旧浏览器软件的用户,任何 CSS 布局书籍都没有用。因此,您还将看到仅使用 CSS2.1 进行布局的最新最佳实践方法,并在此过程中学习概念。尽管我假设你熟悉 CSS2.1,下一章还是从这个主题开始。
摘要
CSS3 仍在发展中,但它的大部分现在都可以安全地使用。像 Flexbox 这样的布局模块为网页设计者面临的一些现实挑战提供了一个可靠的解决方案。自网络出现以来,CSS 已经走过了漫长的道路,在过去的几年中,这种语言的发展速度大大加快。这本书将让你快速掌握开发中每个主要布局模块背后的核心原理,并向你展示如何在现实世界中使用这些模块。我们开始吧!
二、CSS 中的布局模块:旧与新
本章将帮助你在与 CSS 相关的核心布局概念上站稳脚跟。如果你熟悉 CSS2.1,你已经对它了如指掌,但是可能有一些你没有意识到 CSS 支持的概念(因为浏览器支持一直很差)。
本章快速浏览了 CSS 提供的所有不同的布局范例,从 CSS 的原始版本:level 1 开始。请注意,CSS 提供的每个布局解决方案都是作为一个模块实现的。在 CSS3 之前,每一层的整个 CSS 规范都包含在一个无所不包的模块中,而现在每一层都被分割成自己独立的模块;但是为了简单起见,我把每种方法都当作一个单独的模块来讨论。
正如我在第一章中提到的,在 CSS 出现之前,HTML 并没有提供多少布局控制。设计者找到了一种巧妙的方法来制作复杂的布局:通过使用表格在页面上精确定位元素来破解 HTML 规范。表格是一个非常有用的布局工具,因为它们为单个内容的定位提供了一个可控的解决方案。然而,HTML 表格从来就不是用来布局的;所以,从语义上来说,这次黑客攻击是一场灾难。因为整个方法是一种变通方法,所以还存在内容的可维护性和可读性问题。发现六七层深度的嵌套表并不少见。
随着 CSS1 的出现,焦点开始从使用 HTML 呈现一切(包括内容和表示)转移开来。取而代之的是,将内容从审美呈现中分离出来的思想得以确立。
Note
本章讨论了不同的 CSS 级别:1、2 和 3。需要注意的是,在设计网站时,你不能选择使用哪个级别;浏览器支持不同的级别,每个级别都建立在现有级别之上或旁边。虽然这本书是关于 CSS3 的,但是在屏幕上看到的结果取决于你使用的浏览器——或者更重要的是,浏览你网站的人使用的浏览器。
布局概念
在引入 CSS3 之前,CSS 提供了四种(官方)不同的布局模式。正如已经建立的那样,除了用户代理应用的默认行为之外,HTML 本身不包含任何布局特定的功能。对于网页设计者来说,这是一个发现的旅程,从基于表格的布局作为实现对定位的精细控制的方法开始,到本世纪最初几年 CSS 的采用,最终达到今天的成熟程度,表现和可用性的问题是内在相关的。
那么什么是 CSS 呢?当设计者试图创建一个更加视觉化的网站时,HTML 语言的局限性很快就显现出来了。作为一个主要基于文本的系统,HTML 是一个交流信息的好方法,但是它不是一个很好的美化信息的工具。负责开发 HTML 语言的机构做了各种尝试来解决基本的样式需求,但是从根本上说,HTML 的最初目的正在被侵蚀。级联样式表(CSS)的引入是实现可视化 Web 的第一步。一旦建立起来,CSS 就被迅速采用并不断迭代,直到 21 世纪初达到 2.1 版本,当时它陷入了政治和缓慢的浏览器开发的泥潭,陷入了停顿。尽管如此,设计师们继续试验 CSS 的可能性,通过 CSS Zen Garden 这样的工具来推广它,如图 2-1 所示。
图 2-1。
CSS Zen Garden spearheaded the CSS revolution in the early years of the 21st century
网络在发展,但是我们可以使用的工具却没有——直到 CSS3 的出现和实现!CSS 处理 HTML 文档的外观和样式,包括以下内容的表示方面:
- 文本,包括字体选择、字体大小、粗细、间距、方向和装饰
- 不同元素的颜色和背景,包括图像和渐变
- 边框和边框效果,包括线条样式、大小和弯角,以及投影等特殊效果
- 不同元素在页面上的定位,可以在文档流内,也可以在文档流外
- 页面上不同元素的边距和填充
- 跨不同结构元素(包括列和区域)分布和对齐内容
- 过渡和动画,包括用户交互控制
- 2D 和三维空间中的变换
值得快速强调的是,CSS 的最新版本仍然处于不断变化的状态。而在以前的版本(CSS2.1)中,整个规范包含在一个模块中,而在 CSS3 中,各个组件都被模块化了。这意味着 CSS 工作组(CSSWG)可以更快更有效地迭代单个模块,浏览器供应商可以实现标准,而不必等待每个单个模块都达到推荐状态。不利的一面是,规范被分割在许多不同的模块和项目中。这使得跟踪哪个浏览器支持某个特定功能以及全球范围内的最新发展变得很棘手。
自从 W3C 在 2005 年宣布 CSS3 的开发以来,web 设计社区一直带着兴奋和期待的混合情绪关注着,等待浏览器实现这些标准并为设计开辟一系列新的可能性。从 CSS3 开发开始到现在已经将近十年了,但是直到最近一两年才有了对 CSS2.1 的普遍支持!
尽管如此,随着浏览器供应商迅速实现对新功能的支持,CSS3 正在 web 设计界掀起巨大的波澜。所有主流浏览器都已经支持大量新的 CSS3 属性、创新的 CSS3 布局方法、基于 CSS 的动画和特殊的视觉效果。今天,甚至可以在一些浏览器中直接渲染 3D 场景,而不需要插件或特殊的阅读器软件。
历史上,web 采用新技术的速度一直很慢,因为 Web 用户需要在这些技术得到支持之前主动更新他们的浏览器软件。自然,用户有比更新软件更有趣的事情要做,结果整代用户(和计算机)都被购买时安装的原始浏览器所困扰。随着新一代笔记本电脑和台式电脑的出现,更不用说基于 Android 和 iOS 操作系统的智能手机的广泛采用,浏览器软件也在发展,现在许多流行的浏览器在有新版本时会自动更新。
直到几年前,如果不借助 Photoshop、Adobe Flash 或一些非常复杂的 JavaScript,在网页上渲染阴影是不可能的。现在呢?CSS3 使得渲染阴影像设置一个属性一样简单!更好的是,由于这个 CSS3 属性在今天的浏览器中被广泛采用,这是一个非常安全的渲染效果的方法。
CSS3 规范提供了一套强大的新工具,远远超出了简单的投影渲染。这本书关注的是布局选项,但是不要忘记 CSS3 提供的不仅仅是布局控制;CSS3 将你的网页和应用程序的样式提升到了一个全新的水平,远远超越了过去的可能性。当然,它不是一个完美的工具;在本章中,我们来看看 CSS3 没有解决的问题以及它擅长的事情。
不同类型的布局
关于现代 web 开发,需要理解的一个关键问题是内容的结构独立于表现形式。CSS 是专门设计来让你定义视觉或听觉的表现,而不需要以特定的方式构造 HTML,至少是为了如何向用户显示页面。
这并不是说这两件事没有明确的联系!CSS 依赖于它所应用的 HTML 中明确的结构;但是,页面的布局越来越不受 HTML 中内容的顺序或特定标记的支配。
这是非常有益的,因为它允许在稍后阶段更容易地重新调整内容的用途,无论是到新的平台还是作为设计改进的一部分。这也意味着您可以根据设备或访问设备的用户的配置文件,以不同的方式呈现完全相同的内容。在实践中,这就是响应式设计(在不同设备上适当呈现工程内容的过程)。另一个好处是,内容可以按照语义顺序进行组织和交付,而不是依赖于网站的视觉设计。
随着 CSS 语言的发展,可用的布局选项也发生了变化。CSS 的每个新级别都建立在以前级别的基础上,增加了功能和能力。因此,有许多潜在的方法可以用看似相同的布局来设计页面的样式,但是每种方法都有不同的行为和特征。让我们快速看一下在引入新的 CSS 布局模块之前 CSS 提供了什么,这些模块是 CSS3 规范的一部分。
CSS1 中的布局
当网络发展到非常早期的时候,HTML 被用于标记和样式。HTML 属性和标签定义了页面的外观。关于如何标记内容的决定很可能来自标签的默认视觉特征,也可能来自任何语义层次的感觉。<h1>标签用于需要大而粗的文本的地方,<p>用于小文本,<center>标签用于对齐段落和表格——仅作为一种呈现表格数据的方式——以创建复杂的多栏布局,这在语言中可用的标签和格式属性的有限集合中是不可能的。
尽管这种方法非常足智多谋且富有创造性,但它导致了一些可怕的代码,这些代码既难以阅读又难以维护。随着布局需求变得越来越复杂,表格开始相互嵌套,通常有几层之深。视觉上冗余的透明间隔 gif 越来越多地被用来确保元素的正确定位,一个简单的页面包含数百或数千行意大利面条汤(难以理解的代码)变得很常见。清单 2-1 显示了与第一章中相同的代码示例:实践中一个典型页面的 HTML 代码,包括大量可视化渲染指令。
<FONT FACE=TIMES COLOR=#FF0000 SIZE=3>
<H2><I>WELCOME TO MY WEBSITE</I></H2>
<CENTER>
<TABLE WIDTH=720 HEIGHT=480 BORDER=2 BGCOLOR=BLACK>
<TR>
<TD BACKGROUND=texture.gif>
<TABLE WIDTH=360 HEIGHT=480 BORDER=0>
<TR>
<TD>
<IMG SRC=spacer.gif WIDTH=360 HEIGHT=10><BR>
<H1>ABOUT MY SITE</H1>
<P>...</P>
</TD>
</TR>
</TABLE>
</TD>
<TD BACKGROUND=texture2.gif>
<TABLE WIDTH=360 HEIGHT=480 BORDER=0>
<TR>
<TD>
<IMG SRC=spacer.gif WIDTH=360 HEIGHT=10><BR>
<H1>ABOUT ME</H1>
<P>...</P>
</TD>
</TR>
</TABLE>
</TD>
</TR>
</TABLE>
</CENTER>
</FONT>
Listing 2-1.Typical HTML Code Before CSS and Modern Web Standards Were Widely Adopted
这种编码和维护网站的方法所带来的问题对于每个使用 HTML 的人来说都是显而易见的。Web 需要一种方法来将内容与风格分开,因此 W3C 接手了这一工作,开发了 CSS Level 1 规范。
第一章谈到了 CSS 旨在解决的不同视觉特征。回想一下,CSS Level 1 并不打算作为布局的解决方案。取而代之的是,用 HTML 标签和属性取代所有基本的视觉特征。如第一章所述,结果是一组非常有限的属性,包括以下内容:
- 块级元素的宽度和高度
- 漂浮,清除漂浮元素
- 边距和填充
- 背景颜色和图像
- 边界
- 字体和字体样式
- 列表样式
- 一些基本的排列
CSS1 允许设计者在 HTML 代码之外获取单个元素的基本样式。第一次,一个网站的外观可以由一个单独的外部文件来控制。这对页面设计的维护和一致性有很大的好处,因为一行 CSS 代码现在可以影响整个网站。以前,简单的标题颜色改变意味着单独编辑网站的每个页面。
web 浏览器花了一些时间来采用新的 CSS 规范,直到 20 世纪 90 年代末,设计人员才能够依靠常用的浏览器来理解和准确地呈现他们的 CSS 代码。对于历史爱好者来说,这也是微软和网景争夺霸权的时期,Internet Explorer 是最终的胜利者。
值得注意的是,虽然您可能熟悉使用浮动元素作为创建布局的系统的想法,但 CSS1 中引入的float和clear属性从来不是为了这个目的。引入它们是为了提供以前属于 HTML 一部分的ALIGN=LEFT和ALIGN=RIGHT属性的 CSS 传真,旨在用于图像,允许文本自动围绕元素流动。
CSS2 和 CSS2.1 中的布局
CSS2.1 引入并定义了四种不同的布局模式来呈现网页。这些是浏览器在解析 CSS 规则时使用的系统,用于根据元素的兄弟元素、文档中的流和父元素来确定元素的大小和位置。这四种模式如下:
- 块布局:用于布局或组织文档中的元素
- 嵌入式布局:用于布局文本
- 表格布局:用于在二维网格中显示和布局表格数据
- 定位布局:用于在页面上明确定位元素,将它们从文档流中移除
块布局
W3C 的 CSS2.1 规范提供了对块布局的解释:
In the context of block formatting, starting from the top of the containing block, vertically place the boxes one after the other. The vertical distance between two sibling boxes is determined by the margin property. Vertical margin folding between adjacent block-level boxes in block format context.
块布局在元素周围创建矩形框,描述该元素占用的空间量。许多 HTML 元素自动采用块布局,包括像<p>、<h1>、<div>和<ul>这样的元素。块级盒子垂直堆叠,每个盒子紧接着前一个占据垂直空间。块级盒子不会水平堆叠。每个新块出现在新的垂直位置。
有一些与块级框的布局相关联的特殊规则,以及用于确定每个块级框占用多少空间的规则。简言之,这些规则如下:
- 块级元素的
background完全延伸到border的外边缘。这意味着背景填充了内容区域以及padding和border区域的组合。如果边框使用任何透明度(例如,如果它使用虚线),则在虚线之间的空间中可以看到background。 - 块级元素的
width默认设置为auto(填充可用的水平空间)。唯一可以设置为auto的其他属性是margin和height属性。 - 负值可以应用于
margin属性,但是其他属性不能有负值。 width和height属性仅定义内容区域。padding、border、margin都是为了布局的目的而增加盒子的宽度。
这最好用图表来说明,如图 2-2 所示。margin、padding、border都增加了块级元素的位移大小。从历史的角度来看,Internet Explorer 最初实现的盒子模型包括元素的定义宽度(和高度)内的填充,导致了多年的变通方法和特定于浏览器的样式表破解。幸运的是,那些日子已经过去了!
图 2-2。
The block-level box model. The width property affects only the content area, with padding, border, and margin adding to the overall displacement width and height
内嵌布局
内联级元素是源文档中不构成新内容块的那些元素;内容按行分布(例如,段落中强调的文本片段、内联图像等)。内联级元素生成内联级框,它们是参与内联格式上下文的框。这意味着它们在相同的读取流中替换相邻的内容,但是它们不会干扰块级呈现。
内联框是内联级别的,其内容参与其包含的内联格式上下文。一个display值为inline的非替换元素生成一个内嵌框。默认采用内联行为的元素的常见例子包括<span>和<em>。
表格布局
使用display属性进行访问,表格布局允许元素像构成表格的一部分一样工作——承担表格单元格的角色,并占据表格布局中的行和/或列。这种布局模式非常强大,原因和早期的 web 设计者劫持表格一样,但是糟糕的浏览器支持在历史上意味着它从来没有作为一种可靠的布局解决方案出现过。
现代浏览器支持非常好。因此,表格布局是一种可行且有效的布局方法。
定位布局
定位布局允许您使用坐标精确地放置单个元素,相对于页面或另一个具有定义位置的包含元素。这使您可以将元素放在浏览器窗口的右上角,或者让单个元素在视口内的任意位置相互重叠,并假定在文档流中有一个位置是默认行为。
CSS2.1 规范中提供了几种不同的定位属性值:
- 静态:应用正常的流程规则,并且
top、bottom、left和right属性不起作用。 - 相对:框的位置根据它在文档流中的位置来确定。属性然后相对于它的“正常”位置偏移盒子的位置。受框在文档流中的位置影响的后续元素的行为就像框上没有偏移一样。
- 绝对:盒子的位置(可能还有大小)由
top、left、bottom和right属性指定。这些属性指定从包含框(可能是页面本身)的偏移量。 - Fixed:长方体按照绝对位置定位,但它固定在相对于某个参考的位置。在大多数情况下,位置相对于视口是固定的,不会相对于用户滚动页面而移动。
CSS3 中的布局
正如已经讨论过的,CSS3 以一种新的方式处理布局。各个组件被分解成独立的模块,而不是单一的整体规格。因此,CSS3 有几个不同的布局模块,每个模块都是单独开发和维护的。
浏览器已经很好地支持了几个模块;其他的仍在开发和定义中。这本书检查了所有主要的布局模块,并指出哪些模块你可以直接使用,哪些是即将推出的,哪些是未来要关注的。第一章简要地讨论了每一个模块,所以与其再详细讨论每一个模块,不如在这里提醒一下本书所关注的模块:
- CSS 多列布局
- CSS 灵活盒子布局(Flexbox)
- CSS 网格布局
- CSS 区域布局
然而,这个列表并不是 CSS3 中布局的限制。正如你在这本书的结尾所看到的,一些令人兴奋的想法正在被讨论和提出,它们可能会改变你默认的页面布局方式!
理解 CSS3 不会以任何方式取代 CSS2.1 或 CSS1 是很重要的。接下来的章节中涉及的新模块建立在现有规范的基础上。因此,您将继续使用 CSS2.1 引入的四种布局类型——块、内联、表格和绝对——作为最常见的默认布局解决方案。您可以将这些原始布局范例与新模块结合使用,并对其进行补充。
模块化的重要性
您可能想知道为什么 CSS3 以模块化的方式构建很重要,以及这与现有 CSS 规范的工作方式有何不同。简单的回答是,通过采用模块化方法,组成 CSS3 的每个独立模块都可以被浏览器供应商测试、评估和采用,而无需采用整个规范。这也意味着 CCSWG 可以在其生命周期的不同阶段关注规范的不同领域。
这为什么有用?嗯,仍在开发中的新布局模块(例如,CSS 区域布局和 CSS Flexbox)可以与已完成的模块共存。功能可以在持续的基础上发布,而不需要完成每个模块,从而提高了实现和采用的速度。这对大家都好!
我提到过 CSS3 规范的不同部分由不同的模块覆盖,并且每个模块都处于开发生命周期的不同阶段。这听起来像是理想的解决方案,但这并不意味着开发新规范的所有令人沮丧的部分都会被自动删除。仍然有一个固有的缓慢的开发周期,这可能会让热衷于推动 web 前进的 Web 设计人员和开发人员感到沮丧。
然而,您可以从中获得的是,整个规范的不同元素现在都可以使用。相比之下,CSS2.1 过了十年才得到完全支持——而且是在 W3C 的 CSS 团队完成了所有开发工作之后!
一个额外的好处是,因为每个模块都是独立于其他模块的,当一个模块成熟时,浏览器供应商(如微软、苹果、谷歌和 Opera)能够在其浏览器中集成对它的支持。这为新的模块和属性提供了早期支持,但这往往是有代价的;因为当工程师在浏览器中创建支持时,每个新模块都是不完整的,所以新属性在声明之前通常需要一个特定于供应商的前缀。这使得浏览器能够创建早期的支持,而不会导致后来的向后兼容性问题:当最终规范达成一致时,供应商前缀被删除,只留下完整的规范属性名称和行为。
总的来说,CSS3 仍然处于不断变化的状态(我会在整本书中提醒你这一点!).在 CSS2.1 中,整个规范包含在一个模块中,而在 CSS3 中,各个组件都被模块化了。这意味着 CSSWG 可以更快更有效地迭代单个模块,浏览器供应商可以实现标准,而不必等待每个单个模块都达到推荐状态。
CSS 布局的限制
如您所见,CSS2.1 提供了相当多非常有用的布局选项,CSS3 为工具箱带来了大量新选项。这对网页设计师来说是个好消息,随着新技术的发展,你将开始一段激动人心的发现之旅,充分利用新模块提供的机会。
然而,这并不完全是一片美好的前景。光靠 CSS 还是有很多事情做不到的。对于这些任务中的大多数,脚本化的工作区提供了一个解决方案;但是完整的布局涅槃还是有一段路要走。本书的最后几章对一些即将到来的提案进行了短暂的浏览。
正如您所看到的,CSS 主要关注页面的美学表现,无论是通过屏幕上内容的视觉表现,还是在办公室激光打印机上打印页面的方式,或者屏幕阅读器如何强调内容。对于这项任务来说,CSS 是网页设计者可用的最佳解决方案,在可预见的未来也是如此。如果你想做的只是创建静态网页,并把它们漂亮地呈现出来,CSS 将会是最合适的工具。
你可能已经猜到了,有一个“但是”来了!在今天的网站中,很难找到不需要某种文档对象模型(DOM)操作(移动、添加或删除树中的元素)或过程化生成标记的页面。有时,您创建完全通过服务器端脚本语言(如 ASP.NET 或 PHP)呈现的页面,使用处理逻辑生成按需呈现和样式化的页面。
CSS 可以很好地处理这种情况,提供了处理不同数量或排列的标记的灵活性。然而,它不是为设计者试图减少或消除回发到服务器进行数据处理的需要而设计的。
让我们以一个带有搜索表单的页面为例,当用户开始输入时,搜索表单会返回结果。现在,您不必依赖于页面首次加载时呈现的所有内容,而必须考虑到内容可能会随时发生变化,以响应动态加载的新内容。
Note
在继续之前,我应该指出,这种交互式数据反应用户界面解决方案与传统的页面加载/页面呈现模型一样频繁地向服务器回发数据。是生成和呈现内容的方法发生了变化。
内容通常使用 JSON 或 XML 通过 JavaScript 以这种方式加载。该脚本调用一个服务器方法,该方法或者为正在读取的页面生成附加标记,或者以脚本可以解释并用于操作页面内容的格式输出数据。
无论以何种方式加载内容,页面上显示的数据量和数据类型都会随着事件的发生而改变。当然,在创建样式表时,您可以考虑所有可能的场景,但是仅仅使用 CSS 是无法实现表示的细微差别以及向用户展示新内容已经加载到页面中的。
对于这些情况,JavaScript 是操纵页面外观的最佳方式。通常情况下,JavaScript 用于更新应用于特定元素的 CSS,但是程序性地生成值和属性的能力将脚本化解决方案与纯 CSS 方法区分开来。
如果单独使用 CSS 无法实现特定的布局,那么现在使用 JavaScript 为缺失的行为提供多种填充是完全可以接受的。我将在每个模块章节的末尾介绍这些场景。
Note
只是提醒一下:本书中的一些模块处于不断变化的状态。请经常查看 W3C 网站上的最新规范,以确保您使用的是最新的语法。
总结:为未来做好准备!
本章研究了不同层次的 CSS 提供的布局选项。CSS1 主要为内容提供了样式选项,忽略了控制这些元素位置的需要。第 2 级带来了四种不同的布局方法,用于相对于页面以及元素之间的定位。CSS3 远远超越了这四个工具。
现在,让我们继续学习新的模块,以及如何使用它们来创建新的布局,这在以前单独使用 CSS 是不可能的。下一章将向你展示 CSS3 之前的布局模块的概要,之后就是 CSS3 了。让我们开始吧!
三、我们去过的地方:定位、浮动和显示
本章介绍 CSS2.1 和 CSS1 布局模块。它既是现有布局选项的总结,也是基于 CSS 布局的“老式”方法的最佳实践指南/参考指南。有经验的网页设计师可能会选择跳过这一章,但是你可能会错过一些有用的技术和方法,即使是最老练的网页设计师也可能不知道。本章包括以下内容:
- CSS 布局介绍
- CSS2.1 布局模块
- 如何使用它们
- 真实世界的例子
您将快速浏览 CSS 提供的所有布局范例,从 CSS 的原始版本:Level 1 开始。请注意,CSS 提供的每个布局解决方案都是作为一个模块实现的。在 CSS3 之前,每个层次的整个 CSS 规范都包含在一个无所不包的模块中,而现在每个层次都被分割成自己独立的模块;为了简单起见,我把每种方法都当作一个单独的模块来讨论。
正如在第一章提到的,CSS 之前,HTML 在布局控制方面没有提供太多。设计者找到了一种巧妙的方法来制作复杂的布局:通过使用表格在页面上精确定位元素来破解 HTML 规范。表格是一个非常有用的布局工具,因为它们为单个内容的定位提供了一个可控的解决方案。然而,HTML 表格从来就不是用来布局的;所以,从语义上来说,这次黑客攻击是一场灾难。由于整个方法是一种变通方法,所以还存在内容的可维护性和可读性问题。发现六七层深度的嵌套表并不少见。
随着 CSS1 的到来,焦点开始从使用 HTML 渲染一切转移开来。取而代之的是,将内容从审美呈现中分离出来的思想得以确立。
Note
如果你已经是 CSS2 布局的专家,这一章不会给你提供任何新的见解,所以可以直接跳到第四章。如果你是网页设计新手,请继续阅读 CSS2 和 CSS1 提供的布局选项。
CSS3 之前的布局
正如在第二章中提到的,除了根据语义层次标记内容的能力之外,HTML 不是为布局而设计的。网络的第一个版本仅仅是为了方便访问和检索而互相链接的科学文献的集合。HTML 语言确实发展到以对齐的形式加入了一些有限的布局选项,但在早期,布局主要是通过误用<table>标签实现的。
想象一下这样一个场景:您想要为 Web 创建一个布局,其特征不仅仅是像 Word 文档一样的一系列内容。你的设计需要两栏(现代的说法是侧栏)。使用直接的 HTML 这实际上是不可能的,直到有人注意到<table>标签提供了这种程度的布局控制。图 3-1 显示了一个没有任何“布局”的布局,旁边是一个使用表格创建“布局”的布局很容易看出哪一个在视觉上更有吸引力——难怪设计师们想尽一切办法来美化和拓展网页设计的边界。
图 3-1。
A linear layout versus a table-based layout , demonstrating the attraction of using tables when there were no other options available to designers
通过使用表格来排列不同的内容,设计者能够精确地控制元素在页面上的位置。尽管表格是为表示表格数据集而设计的。这种方法曾一度奏效,但随着设计师开始寻求更具进步性的设计,布局革命变得不顺利。使用表格可以实现极其复杂的布局,但是设计越复杂,就越有可能需要表格中的表格和跨越多列或多行的单元格;这导致了一种可怕的意大利面条式代码,难以破译,维护起来也很糟糕。
然而,难以理解的代码并不是表格独有的问题。作为将风格与内容分离的更广泛策略的一部分,W3C 致力于设计 CSS。直到 CSS2 才出现了任何特定于布局的特性;本章介绍了这些功能。
第二章列出了布局范例。本章从实际应用的角度讨论了所有的选项,所以让我们把浮动布局添加到列表中。在 CSS3 到来之前,可用的 CSS 布局选项如下:
- 块布局:用于在布局文档时控制框
- 嵌入式布局:用于布局文本
- 表格布局:用于在二维网格中显示和布局表格数据
- 相对和定位布局:允许在页面上显式定位元素,或者对文档流做出反应并影响后续元素,或者将它们从文档流中移除并使它们对页面上的周围元素没有影响
- 浮动布局:允许元素在出现时从文档流中移除,但仍会影响页面上相对定位的元素
如果您对表格布局感到困惑,这是可以理解的。使用表格进行布局的想法从来没有问题,但是 HTML 表格实现并不是为适应布局而设计的。所以 CSS 2.1 规范引入了表格布局作为布局范式。让我们来看看实现布局的每一个选项,包括一些基本的例子,如果你是网页布局的新手,这些例子可以帮助你迅速进入角色。
相对和绝对定位
默认情况下,页面上的 HTML 元素采用相对定位。这意味着它们会将其他元素挤出来,为它们在文档中占据的位置腾出空间。如果您选择为某个元素分配绝对定位,您将从普通文档流中删除该元素,并且它不再与页面上的其他元素争夺位置。此外,相对定位的元素作为页面上的在先元素的结果被放置在页面上,而绝对定位的元素可以被分配坐标来占据。看看清单 3-1 中的代码,看看这两个选项是如何工作的。
<style>
.relative {
position: relative;
width: 100px;
height: 100px;
background: red;
margin: 10px;
}
.absolute {
position: absolute;
top: 100px;
left: 200px;
width: 100px;
height: 100px;
background: yellow;
margin: 10px;
}
</style>
<div class="relative">Element 1</div>
<div class="relative">Element 2</div>
<div class="relative">Element 3</div>
<div class="absolute">Element 4</div>
<div class="absolute">Element 5</div>
<div class="absolute">Element 6</div>
Listing 3-1.Difference between Relative and Absolute Positioning
列表 3-1 的结果如图 3-2 所示:相对定位的元素整齐地堆叠在一起,由于margin属性引入了 10px 的间隙。但是,绝对定位的元素都堆叠在彼此的顶部,因此您只能看到最近绘制的元素(元素 6)。这是 z- index 的一个例子,您可以在元素上设置一个属性,以改变它们在页面上的元素堆栈中的显示顺序,进而改变它们的绘制顺序。
图 3-2。
The result of the code in Listing 3-1
绝对定位元素提供了大量的精细控制,但是当你处理不同大小的屏幕和浏览器窗口时,它们并不总是很方便。通常,您会希望文档流决定元素在页面上的显示位置。值得注意的是,绝对定位使用相对于其父元素的坐标。这意味着如果您将绝对定位的元素放在相对定位的元素中,绝对定位的元素会移动以反映父元素的位置。得心应手!
请看清单 3-2 中所示的示例代码,看看这在实践中是如何工作的。结果如图 3-3 所示。请注意,绝对定位的元素可以呈现在其相对父元素的边界之外。父节点只是提供计算位置的锚点。还值得强调的是,本例中最后一个绝对定位的元素不是位于相对定位的<div>中,而是位于相对定位的<body>中:它看起来呈现在页面的更高处,因为定位坐标是从整个页面的左上角开始计算的,而不是位于页面上其他地方的元素。
图 3-3。
The result of the code in Listing 3-2
<style>
.relative {
position: relative;
top: 100px;
left: 100px;
width: 100px;
height: 100px;
background: red;
margin: 10px;
}
#absolute1 {
position: absolute;
top: 100px;
left: 200px;
width: 100px;
height: 100px;
background: yellow;
}
#absolute2 {
position: absolute;
top: 100px;
left: 200px;
width: 100px;
height: 100px;
background: purple;
}
#absolute3 {
position: absolute;
top: 100px;
left: 200px;
width: 100px;
height: 100px;
background: yellow;
}
</style>
<div class="relative">
Element 1
<div id="absolute1">Element 4</div>
</div>
<div class="relative">Element 2</div>
<div class="relative">
Element 3
<div id="absolute2">Element 5</div>
</div>
<div id="absolute3">Element 6</div>
Listing 3-2.An elaboration on the previous example, showing the difference between relative and absolute positioning
相对定位和绝对定位的结合可以为 Web 上许多不同的布局挑战提供一个好的解决方案,但绝不是所有的挑战。以之前的例子为例,设计师想要将侧边栏合并到页面中。绝对定位的元素会给人一种侧边栏的感觉,但是因为绝对定位从文档流中删除了一个元素,所以侧边栏不会考虑页面上的任何其他内容。它会遮住任何想要呈现在它下面的内容,并且不会对它周围的任何其他内容区域做出反应。在这种情况下,设计师转而使用浮动元素。
浮动布局
浮动元素就是这样做的:它们在父元素中浮动,或者向左,或者向右。父元素中的内容不能侵犯浮动元素占用的空间。相反,内容围绕浮动元素流动,允许它继续影响周围的元素。但是,请注意,浮动元素会将其从页面的大小调整流程中移除。
与绝对位于相对元素内的定位元素一样,浮动不需要保持在父元素的范围内(尽管它们在水平轴上保持不变)。为什么这是一个问题?因为浮动元素可能会从相对定位元素的底部漏出,影响页面上后续相对定位元素的定位。
让我们看一个这种效果的例子。清单 3-3 显示了一个位于相对定位的父元素中的基本浮动元素。清单 3-4 扩展了布局,添加了额外的浮动元素,使它们溢出了父元素的底部。图 3-4 为列表 3-3 的结果,图 3-5 为列表 3-4 的结果。
图 3-5。
The result of Listing 3-4. Note how floating elements do not contribute to the height of the relatively positioned parent element
图 3-4。
The result of Listing 3-3
<style>
.relative {
position: relative;
width: 300px;
background: grey;
margin: 10px;
padding: 10px;
}
#floater {
float: right;
width: 160px;
height: 160px;
padding: 10px;
margin: 10px;
background: green;
}
</style>
<div class="relative">
<div id="floater">Floating element</div>
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Etiam porta sem malesuada magna mollis euismod. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
</div>
Listing 3-3.Continuing to explore the differences between Relative and Absolute Positioning
<style>
.relative {
position: relative;
width: 300px;
background: grey;
margin: 10px;
padding: 10px;
}
.floater {
float: right;
width: 160px;
height: 160px;
padding: 10px;
margin: 10px;
background: green;
}
</style>
<div class="relative">
<div class="floater">Floating element 1</div>
<div class="floater">Floating element 2</div>
<div class="floater">Floating element 3</div>
<div class="floater">Floating element 4</div>
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Etiam porta sem malesuada magna mollis euismod. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
</div>
Listing 3-4.Floating Element within a Relative Parent: Content within the Relative Element Flows around the Floating Element
在 CSS3 之前,浮动元素通常用于创建水平导航栏。但是正如您将看到的,CSS3 中有新的、令人兴奋的(并且更加可靠的)选项。
浮动最大的问题是它们不可预测。有一些变通办法,如清单 3-5 所示,它使用clear属性来指示一个元素仅在所有浮动元素完成渲染后才出现。但是这些变通办法往往会导致页面上出现多余的代码或冗余的元素,比如一个<br>标签仅仅用于扩展一个相对定位的元素以完全包围一个浮动的子元素。同样值得一提的是,浮动元素不会影响结构布局,所以使用浮动元素创建的侧边栏在高度上无法与其父元素匹配,除非求助于黑客或脚本。
<style>
.relative {
position: relative;
width: 300px;
background: grey;
margin: 10px;
padding: 10px;
}
.floater {
float: right;
width: 60px;
height: 60px;
padding: 10px;
margin: 10px;
background: green;
}
.clearfloat {
clear: right;
}
</style>
<div class="relative">
<div class="floater">Floating element 1</div>
<div class="floater clearfloat">Floating element 2</div>
<div class="floater">Floating element 3</div>
<div class="floater ">Floating element 4</div>
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Etiam porta sem malesuada magna mollis euismod. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
<br class="clearfloat" /> <!-- this isn't necessary on this page, but is commonly used to ensure the <div> with a class of relative expands to encapsulate the floating elements -->
</div>
Listing 3-5.Using the clear Property to Ensure that the Second Floating Element Doesn’t Stack Next to the First
图 3-6 显示了列表 3-5 的结果。属性强制周围的元素考虑浮动;但是正如您在最后一行代码中看到的,这可能会导致额外的元素被引入到标记中,仅仅是为了解决浮动的限制。这可不是什么好事!毕竟,CSS 旨在促进样式和内容的分离。
图 3-6。
The output of Listing 3-5
先不说警告,浮动元素肯定有它们的用处。出现在文本块中的图像、引用、节内导航和旁白都是float属性的有效用法,所以不要把它当作布局工具。
到目前为止,本章讨论的所有内容都依赖于所讨论的元素在页面上的大小和位置。这就是所谓的块显示,这也是下一节要讨论的内容。
块和内嵌显示
页面上的元素有两种不同的显示类型,任何特定元素的默认显示样式取决于其语义功能。让我们看看每个术语的含义:
- 块显示意味着元素占据水平和垂直空间,用一个不可见的边界框包围它。块元素替换页面上的其他元素,导致它们移动位置以适应其边界框的空间。块级元素总是从新的垂直空间开始,并占据全部可用宽度,尽可能地延伸。
- 内联显示意味着元素位于内容流中,占据水平空间,但不影响垂直布局,除非元素的比例导致任何位移。
图 3-7 中的图表显示了两种显示类型之间的差异。
图 3-7。
Differences between block and inline display, and how each impacts layout Note
请记住,当我说垂直和水平时,我假设您正在使用从左到右、从上到下的拉丁语。CSS 现在也支持其他阅读方向,但是为了避免混淆,我将这些术语作为页面上使用的阅读方向的同义词。
还有一种你没见过的展示:没有。如果将display属性设置为none,该元素会保留在页面上,但不会被渲染引擎绘制,也不会对周围的元素产生任何位移。您可能想知道何时需要使用display : none,因为排除 HTML 代码肯定比使用 CSS 隐藏元素更有意义。考虑一下弹出式模态对话框、页面的“阅读更多”部分和下拉菜单系统。将display属性的值设置为none是一种非常有用的能力,没有它,你在网上遇到的很多交互都是不可能的!(如果你真的需要隐藏一个元素,但仍然让它占据页面上的空间,使用visibility: hidden而不是display: none。当你准备好再次展示时,使用visibility: visible。这将继续渲染对象,但阻止它在屏幕上绘制,允许它继续占用布局中的空间。)
让我们看一些块元素和内联元素的例子。表 3-1 显示了每种情况的一些常见示例。
表 3-1。
Examples of Elements that Are Either display: block or display: inline by Default (CSS Allows You to Overwrite This Default)
` | `` |
| `` | `` |
| `` | `` |
| `
` | |
| `
` | `` |
| `- ` | `` |
| `` | `` |
| `` | `
` |
除了display: block、display: inline和display: none之外,还有另一个选项,它结合了display: block和display: inline. Display: inline-block的属性,在两者之间提供了一个折衷方案,允许元素内嵌放置,但占用指定的宽度和高度。Inline-block相对来说是 CSS 党的后来者,主要是因为在 CSS2.1 的早期,浏览器没有可靠地支持它。但它是另一个有价值的工具,也是帮助解决布局挑战的一个选择。清单 3-6 展示了display: inline-block的一个简单例子。结果如图 3-8 所示。

图 3-8。
The output of Listing 3-6, where elements have been given structure thanks to display: inline- block
<style>
.relative {
position: relative;
padding: 10px;
width: 500px;
background: red;
margin: 10px;
}
.navitem {
display: inline-block;
width: 70px;
height: 40px;
line-height: 40px;
text-align: center;
background: grey
}
</style>
<div class="relative">
<h1>Example of inline-block</h1>
<p>Here is some content <span>that includes an inline <span> element</span>.</p>
<p>Here's some more content, and this time the <span> elements have the class navitem applied to them: <span class="navitem">Item 1</span> <span class="navitem">Item 2</span> <span class="navitem">Item 3</span></p>
<p>Note that inline-block elements contribute to the layout.</p>
</div>
Listing 3-6.Using inline-block to Give Inline Elements Structure in the Form of Width and Height
显示属性如何影响布局?
如您所见,您对页面上元素的绘制方式有很大的控制权。display属性直接影响布局。默认情况下,一些元素的width和height不能被显式设置,因此它们不能影响周围的元素或它们在页面上的位置。其他的默认有影响。自然,您可以使用 CSS 中的display属性覆盖任何元素的默认属性。
让我们快速地看一下它的运行情况。清单 3-7 结合了相对和绝对定位;浮动元素;和内联块元素合并到一个页面中。这并不罕见;你通常会在最复杂的布局中找到各种布局的例子。列出 3-7 的结果如图 3-9 所示。

图 3-9。
The output of Listing 3-7. Note that different layout paradigms are suited to different solutions, so it’s common to use a range of tools to achieve a specific part of the overall layout
<style>
.relative {
padding: 10px;
position: relative;
width: 920px;
background: #efefef;
border: 1px solid #ccc;
margin: 10px auto;
}
#header {
position: relative;
padding: 10px;
height: 80px;
line-height: 80px;
background: #999;
color: #fff
}
#header h1 { font-weight: normal;margin:0;padding:0;}
#search {
position: absolute;
top: 10px;
right:10px;
}
#search input {
padding: 5px;
}
#nav {
position: relative;
height: 30px;
background: #ccc;
}
#nav ul {
margin: 0;
padding: 0;
}
#nav ul li {
float: left;
list-style:none;
padding: 0;
margin: 0 3px;
width: 70px;
height: 30px;
line-height: 30px;
text-align: center;
background: #ddd;
}
.sidebar {
float: right;
width: 200px;
background: #ebebeb;
padding: 10px;
}
.ib {
display: inline-block;
width: 70px;
line-height: 30px;
text-align: center;
background: #ddd;
border: #ccc;
margin: 5px;
}
.clearfloat {
clear: both;
}
</style>
<div class="relative">
<div id="header">
<h1>Site Name</h1>
<form id="search"><input type="text" value="search site" /></form>
</div>
<div id="nav">
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">About</a></li>
<li><a href="#">Contact</a></li>
</ul>
</div>
<div class="sidebar">
<h3>Popular CSS modules</h3>
<p><span class="ib">CSS Flexbox</span><span class="ib">CSS Multi-Column</span><span class="ib">CSS Regions</span><span class="ib">CSS Grid Layout</span><span class="ib">CSS Shapes</span></p>
</div>
<div id="content">
<h2>Welcome to our website</h2>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Curabitur blandit tempus porttitor. Maecenas faucibus mollis interdum. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum.</p>
<p>Donec id elit non mi porta gravida at eget metus. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Curabitur blandit tempus porttitor. Donec sed odio dui. Maecenas sed diam eget risus varius blandit sit amet non magna.</p>
<p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Nulla vitae elit libero, a pharetra augue.</p>
<p>Sed posuere consectetur est at lobortis. Sed posuere consectetur est at lobortis. Maecenas sed diam eget risus varius blandit sit amet non magna. Nullam id dolor id nibh ultricies vehicula ut id elit.</p>
</div>
<br class="clearfloat" />
</div>
Listing 3-7.Combining relative, absolute, float, inline, block, and inline-block Elements into One Example Layout
表格布局
本章讨论的最后一个布局工具是表格布局。正如你在第 1 和 2 章中所看到的,从网络的早期开始,表格就被用来实现布局。一旦 CSS 变得可用和可靠,Web 上就出现了强烈的反表格布局运动,结果表格布局几乎成了禁忌。这是一种基于 HTML 中的表格仅仅是为了使用表格数据而形成的观点,而不是为了页面的布局和显示。
因此,区分 HTML 表格和 CSS 表格是很重要的。这里讨论的表格布局是通过将display: table分配给一个容器元素实现的。将display: table应用到现有的页面元素和使用 HTML <table>元素的区别在于,根据定义,<table>元素是并且将永远是一个表格。将display: table设置为属性的<div>可以简单地通过编辑 CSS 代码或使用@media查询来更改为使用不同的布局范式。CSS 表格有利于布局!
CSS 表格布局提供了一种基于样式的解决方案,以类似于 HTML 表格但又不完全相同的方式呈现内容。每个不同表格元素的相应属性值如下:
| `table { display: table }` |
| `tr { display: table-row }` |
| `thead { display: table-header-group }` |
| `tbody { display: table-row-group }` |
| `tfoot { display: table-footer-group }` |
| `col { display: table-column }` |
| `colgroup { display: table-column-group }` |
| `td, th { display: table-cell }` |
| `caption { display: table-caption }` |
清单 3-8 展示了如何使用其中的每一个来实现基于表格的布局,以及该方法提供的所有好处。结果如图 3-10 所示。

图 3-10。
A page layout achieved using display: table. This is practically impossible to achieve without table layout, unless you use scripts or CSS3! Note that some properties have been ignored and aren’t rendered in the final page. display: table works just like an HTML table!
<style>
.table {
display: table;
border: 1px solid red;
background: #eee;
margin: 2px;
padding: 2px;
}
.tr {
display: table-row;
border: 1px solid red;
background: #ddd;
margin: 2px;
padding: 2px;
}
.td {
display: table-cell;
border: 1px solid #red;
width: 200px;
background: #ccc;
margin: 2px;
padding: 2px;
}
</style>
<div class="table">
<div class="tr">
<div class="td"><p>This is a normal div, but displays like a cell in a table!</p></div>
<div class="td"><p>This is another table cell</p></div>
</div>
<div class="tr">
<div class="td"><p>Here is a third cell, in a second row</p></div>
<div class="td"><p>And a final cell.</p></div>
</div>
</div>
Listing 3-8.Creating a Layout Using display: table Properties to Achieve Something that’s Very Difficult Using Alternative CSS2.1 Layout Options
CSS 表格布局是一个非常有用的布局工具,是无数场景中的最佳选择。在 CSS3 出现之前,实现三列布局是一个很好的选择,其中各个列匹配最长列的高度,而不需要脚本或黑客。这也是一种垂直对齐内容的有用方式,而不必求助于负边距或其他类似的解决方法。但是这本书是关于 CSS3 的——让我们不要花太多时间担心display: table。您可能会遇到这种情况,作为一种潜在的布局工具,它当然不容忽视,但您阅读本书并不是为了了解基于表格的布局!
本章的要点是 CSS 布局模块不是孤立存在的。你几乎不可能遇到一个设计不包含 CSS 布局选项的样式。成功的设计师使用最好的工具,所以在设计页面时,不要害怕调用 CSS2.1 布局模块。在设计元素中找到最适合您的特定需求的布局模块,并使用它。
摘要
在本章中,您已经看到了 CSS3 之前可用的布局选项的高级概述。一些基本的例子有助于解释这些选项,本章还谈到了使用这些模块的一些布局限制。设计网站时,您每天都会用到所有这些布局模块;因此,如果你的胃口被激起,你想进一步阅读,去看看涵盖 CSS2.1 的书籍。
现在你已经准备好使用 CSS2.1 创建布局了。让我们直奔主题,开始学习新的东西:是时候探索 CSS3 布局了!
四、CSS 多列布局
CSS 多列布局模块提供了一个解决方案,这个方案解决了 web 设计者从 Web 诞生之初就面临的一个基本布局问题:如何排列内容,使其占据多个垂直容器,如报纸或杂志。多年来,聪明的网页设计者开发了各种变通方法来创建多栏布局。最初,这涉及到使用表格来对齐内容列,就好像每个元素都是单元格中的一个值一样。最近,浮动元素、清除元素和偶尔的 JavaScript 的巧妙组合提供了更具语义的解决方案,但这些方法都不太适合创建多栏布局。新的 CSS3 模块克服了许多与生成这种布局相关的问题,自动处理内容的流动和分发;而且浏览器支持已经相当不错了,所以今天开始使用这个模块还是相对安全的!
Note
虽然浏览器的支持已经很好了,但是浏览器厂商还在实现这个模块。所有常见的警告都适用!
什么是 CSS 多栏布局模块?
CSS 多列布局提供了一种将内容区域划分成列的解决方案,内容可以自动分页。与本书中涉及的其他全新布局模块不同,多列布局模块扩展了现有的 CSS box 模型。因为新模块建立在现有布局范例之上,所以回退是自动处理的。不理解多栏布局属性的浏览器会忽略它们,将内容区域呈现在单个栏中,而不是呈现为多个栏;见图 4-1 。

图 4-1。
CSS Multi-column Layout in action versus a fallback non-columnar layout in the same container
语法和结构
CSS 多列布局模块扩展了现有的 CSS 框模型,总共增加了十个属性。其中的每一个都提供了对内容呈现到容器中的列的方式的一个方面的控制。
因为所有属性都影响单个容器,所以从浏览器的角度来看,该模块非常容易理解,而且更重要的是,实现起来非常简单。因此,“在野外”对这个模块的支持是极好的!如果您的用户使用他们选择的最新版本的浏览器,您的内容将以列的形式呈现,每个常用的浏览器都支持此模块。
Note
与任何新的 CSS3 布局模块一样,在所有浏览器和操作系统组合中进行彻底测试总是值得的。您可以在 http://caniuse.com/ 查看最新的浏览器对 CSS 多栏的支持表。
基本概念
CSS3 多列布局模块引入了十个可以应用于块级元素的新属性。除了控制列数、分布、宽度和分段的新属性之外,还有一些新的关键字可用于帮助控制内容跨列拆分的方式。
使用多栏布局模块创建多栏布局非常简单。没有不必要的复杂属性需要学习;因为所有事情都发生在单个元素中,所以除了你熟悉的 CSS2.1 的规则之外,没有必要担心内容如何在容器外包装。
仅使用这十个属性,您就可以执行以下操作:
- 将现有的单列元素转换为多列元素
- 内容自动跨列换行
- 控制列填充可用空间的方式,或者通过扩展以填充容器,或者始终遵循预设大小(这对于创建响应性布局很方便)
- 定义出现在列之间的边框和装订线宽度
- 呈现内容,使其跨越多列,以便在需要时打破分栏布局
关于溢出内容的呈现方式以及内容跨列分隔的方式,有一些特殊的规则。你将在本章的后面详细了解这些。
值得一提的是 CSS 多列布局允许您做的事情列表中的第三项。开箱即用,CSS 列可以快速响应。这意味着,如果您希望针对多种不同的设备配置对内容进行定位和分页,这是一个理想的布局模块。有很多方法可以打破这种固有的响应能力,但接下来的几页将重点介绍需要注意的事项。
CSS 列的响应特性不仅限于文本。图像也遵循分栏布局,因此通过使用典型的响应图像技术,您还可以使图片缩放以适合用于查看页面的屏幕。这是一个很好的节省时间的方法,也是值得你花时间熟悉这个模块的另一个原因。
我希望你相信 CSS 多列布局是你布局武库中的一个有用工具。让我们深入研究一下代码,看看它是如何工作的。
理解术语
与本书中涉及的一些其他布局模块不同,CSS 多列布局模块使用了我们都熟悉的现有布局范例。这使得理解和开始在你的布局中使用模块变得非常容易。也就是说(以防你对列的概念不太熟悉),让我们快速了解一下列这个词在 CSS 环境中的含义。
列是在整个容器中呈现内容的垂直划分。一个容器可以包含一列或多列,内容根据所用语言的规则从一列流向下一列。如果您用英语创作,列从左到右呈现。当内容溢出第一列时,它开始在下一列的顶部呈现。当所有的列都被使用时,根据正常的 HTML/CSS 规则,内容会溢出,尽管这可能会导致额外的列——您稍后会看到这一点。
CSS 多列模块的好处之一是,默认情况下,容器的高度会自动计算以容纳所有内容。这很有用,也是使用该模块优于一些旧的变通解决方案的主要好处之一。开箱即用,内容还会自动在所有列之间平衡,从而产生一个令人满意的内容块,它被划分为整齐的列,以符合您的规范。
图 4-2 展示了 CSS 多栏布局讨论中使用的不同属性。你可能会很快学会,因为它很直观;但是如果你不确定column-fill和column-span之间的区别,这个数字还是值得参考的。

图 4-2。
The terms used to talk about multi-column layouts in CSS3
在全神贯注于理论和财产名称/价值之前,让我们看一个例子。基本多栏布局的代码如清单 4-1 所示,您可以在图 4-3 中看到一个输出示例。在接下来的几页中,我将解释这个例子中发生了什么。注意,与所有使用供应商前缀属性实现的新兴模块一样,您可能需要添加-webkit-或-moz-前缀来使其显示在您的浏览器中。

图 4-3。
The resulting appearance from the combination of HTML and CSS code in Listing 4-1, shown at normal desktop resolution of greater than 960px width
<style>
.multicol {
position: relative;
margin: auto;
max-width: 960px;
columns: 4 12em;
column-gap: 2em;
column-rule: 1px solid red;
}
.multicol p {
padding: 0.25em;
}
.multicol figure {
margin: 0;
padding: 0;
width: 100%;
}
.multicol figcaption {
color: #999;
font-size: 0.7em;
}
.multicol img {
width: 100%;
}
.multicol h1 {
column-span: all;
margin: 0.25em 0;
padding: 0;
}
.multicol h2 {
margin: 0.25em 0;
padding: 0;
}
</style>
<article class="multicol">
<h1>Moby Dick; Or the Whale</h1>
<h2>by Herman Melville</h2>
<figure>
<img src="img/mobydick.jpg" alt="Cover of Moby Dick" />
<figcaption>Voyage of the Pequod, illustrated by Everett Henry</figcaption>
</figure>
<p>Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little ... How then is this? Are the green fields gone? What do they here?</p>
</article>
Listing 4-1.HTML Markup Being Styled with CSS Multi-column Layout Properties
这个例子真正有用的地方在于它能立即响应,正如你在图 4-4 和 4-5 中看到的,我已经缩小了浏览器窗口的宽度来模拟更小的屏幕。让我们看看这个例子的不同元素。

图 4-5。
The result of Listing 4-1 rendered at 320px wide—the most common smartphone resolution

图 4-4。
The same rendering shown with a reduced browser window width Note
需要一些额外的非多栏布局代码来定义图 4-3 、 4-4 和 4-5 中所示的颜色、边框和印刷样式。我还删减了清单 4-1 中标记的段落内容以节省空间。
HTML 标记
我不需要谈论 HTML,因为您正在使用标准的语义元素作为内容的容器。使用 CSS 多列布局没有特殊的标记要求;使用此模块,任何块级元素都可以应用多个列。
多列模型
关于图 4-3 和清单 4-1 中所示的例子,有一些事情需要注意,特别是关于内容如何在容器中呈现。在最初的 CSS 盒模型中,元素的内容流入该元素的内容盒。CSS 多列布局引入了一种新类型的容器,它存在于内容框和实际内容之间。W3C 称之为列框(简称为 column)。内容在一个元素内自动跨列流动,以内嵌(或阅读)方向流动。在基于拉丁语的语言(如英语)中,这是从左到右的。
列被排列成行。一行中的所有列都有相同的宽度和高度。列之间可以有间隔,这被称为列间距(或印刷术语中的装订线)。在大多数屏幕情况下,拆分成列的元素只有一行。但是,在特殊情况下,一个元素可能包含多行列。打印的文档也可以由多行组成,其中一个内容区域占据多个打印页面。在本章的后面,您将看到一个屏幕上多行的示例。
我在解释 CSS3 多栏布局时使用 W3C 的语言。出于这个原因,当谈到应用了多列属性的容器时,我引用了multicol元素。多列(multicol)元素是其column-width或column-count未被设置为auto的任何容器。如您所见,在这种情况下,我使用了一个<article>元素。为了避免混淆,我也使用multicol作为属于这个元素的类的名称。
下一节中涉及的所有单个属性都应用于multicol元素。在 CSS3 中,不可能在单个列上设置属性和值。明确地说,这意味着您不能为某一列指定background、padding或margin。
Note
应用于multicol元素的Padding和margin被应用于容器,而不是它的列
虽然您不能控制单个列,但理解浏览器呈现器将它们视为独立的块级框(就像表格单元格一样)是很重要的。每个列框充当其内容的包含块。这意味着,例如,应用于在列中呈现的段落元素的margin和padding被应用于该列的包含块的边缘。
CSS 属性
查看清单 4-1 和图 4-3 、 4-4 和 4-5 中所示示例的 CSS 代码,看起来不熟悉的第一段代码应该是使用了columns属性。这是为两个 CSS 属性定义值的简写(参见清单 4-2 )。
.multicol {
columns: 4 12em;
}
Listing 4-2.Defining the <article> with a class of multicol as Having Four Columns, Each of Width 12em
正如我刚才提到的,columns属性是两个 CSS 属性的简写:column-count和column-width。在这种情况下,我已经为.multicol元素分配了四列,每列的宽度为12em。需要注意的是,浏览器不一定会完全呈现您指定的内容。如果没有足够的空间来创建四列,每列的宽度为12em,渲染引擎会删除列以使其适合。结果宽度可能不是12em,列数可能不是四列!
这意味着当您使用 CSS 多列布局模块定义列时,您不能确定浏览器将准确呈现您直观期望的内容。这听起来像是一个糟糕的实现;但是正如您将看到的,它是该模块最强大的方面之一的基础:CSS3 多列布局在默认情况下是响应性的。
列计数
column-count定义一个multicol元素应该被分成的列数。正如您将看到的,规则决定了浏览器是否尊重该属性。如果未指定或设置为auto,则根据column-width属性值划分空间。
列宽
column-width指定了每列在multicol元素中应该占据的最小宽度。注意,我说的是最低限度,不是最终的!同样,渲染引擎遵循一系列逻辑规则来确定是否严格遵守该规则。如果未指定或设置为auto,列宽由拆分成的列数决定——根据column-count属性值。
列
columns属性是组合column-count和column-width属性的有效快捷方式。根据您传递给columns属性的值,浏览器会以不同的方式解释您的意图,因此理解所有可能的排列很重要。我在代码清单 4-3 中借用了 W3C 的示例代码来帮助说明。
记住这是如何工作的一个简单方法是,如果你使用单个值和一个单位,浏览器会将其解释为一个column-width值。如果省略一个单位,该值将被解释为一个column-count值。如果您不确定,请指定两个值:一个有宽度单位,一个没有计数单位。
columns: 12em; /* equates to column-width: 12em; column-count: auto */
columns: auto 12em; /* equates to column-width: 12em; column-count: auto */
columns: 2; /* equates to column-width: auto; column-count: 2 */
columns: 2 auto; /* equates to column-width: auto; column-count: 2 */
columns: auto; /* equates to column-width: auto; column-count: auto */
columns: auto auto; /* equates to column-width: auto; column-count: auto */
Listing 4-3.Effective Longhand Version of Each of Six Property Values Assigned Using the columns Shorthand Property
删除列和更改宽度的规则
如前所述,浏览器的渲染引擎并不总是严格遵循您对multicol元素的宽度和总列数的定义。W3C 的 CSS3 多列布局模块规范定义了如何以及何时改变呈现意图的规则。W3C 提供了一个伪代码列表来显示要应用的逻辑。我在清单 4-4 中复制了这个;上市后可以找我翻译的规则,不用担心看不懂这段代码!
if ((column-width = auto) and (column-count = auto)) then
exit; /* not a multicol element */
if ((available-width = unknown) and (column-count = auto)) then
exit; /* no columns */
if (available-width = unknown) and (column-count != auto) and (column-width != auto) then
N := column-count;
W := column-width;
exit;
if (available-width = unknown) then
available-width := shrink-to-fit;
if (column-width = auto) and (column-count != auto) then
N := column-count;
W := max(0, (available-width - ((N - 1) * column-gap)) / N);
exit;
if (column-width != auto) and (column-count = auto) then
N := max(1, floor((available-width + column-gap) / (column-width + column-gap)));
W := ((available-width + column-gap) / N) - column-gap;
exit;
if (column-width != auto) and (column-count != auto) then
N := min(column-count, floor((available-width + column-gap) / (column-width + column-gap)))
W := ((available-width + column-gap) / N) - column-gap;
Exit
Listing 4-4.W3C’s Pseudocode for Calculating the Width and Number of Columns According to the Values Set for the column-width and column-count Properties
下面是我对清单 4-4 中伪代码的翻译。请注意,规则和条件是按照以下顺序进行比较和操作的:
If the column-count and column-width properties are both either unset or set to auto, no columns are rendered. The same is true if column-count is set to auto and the width of the multicol element is unrestricted. If the column-count and column-width properties both have values and a width hasn’t been specified for the multicol element, use the values exactly as they are specified. If the column-count property has been set with a value other than auto and the column-width property has been set to auto, divide the available width into the number of columns specified. If the column-width property has been set with a value other than auto, the column-count property has been set to auto, and the container has a specified width, set the number of columns to match the available space divided by the specified width of each column. This may mean the columns end up wider than specified, if the width of the multicol element doesn’t divide perfectly by the specified width of each column. If the column-count and column-width properties have both been set with a value other than auto and the width of the multicol element has been set, look at whether the combination of width and count will fit inside the container element. If not, reduce the number of columns to match the number of times the specified width of each column will fit in the available space, and then expand the column width to fill the multicol element using the calculated column-count.
我希望您可以看到这种方法不仅有意义,而且使 CSS3 多列布局模块非常灵活。它被设计为自动响应,同时尽可能接近地尊重指定的column-count和column-width属性的值。
列间隙
如果你关注清单 4-4 中的代码,你会发现一个我还没有讨论的新属性:column-gap. column-gap用于定义列之间的装订线宽度。图 4-3 中的例子使用了column-gap。您可以在清单 4-5 中再次看到.multicol元素的完整定义。
.multicol {
position: relative;
margin: auto;
max-width: 960px;
columns: 4 12em;
column-gap: 2em;
column-rule: 1px solid red;
}
Listing 4-5.Stylesheet Definition for the .multicol Element, Showing the column-gap Property in Use
与column-width和column-count属性不同,column-gap属性是绝对的。这意味着如果您指定了一个值,浏览器将尊重该设置,即使这意味着牺牲列。
列规则
属性控制是否在呈现的列之间画一条分隔线。Column-rule的工作方式与 CSS 1 和 2.1 中的border属性非常相似。事实上,与border属性一样,column-rule是三种不同属性的简写属性:
Column-rule-width:和border-width一样,这个属性接受一个单位的参数,比如2px,或者关键字none。
Column-rule-style:设置要渲染的线条样式。与border-style相同的选项可用,包括dotted、dashed、solid等。
Column-rule-color:该颜色属性可以使用浏览器支持的任何颜色模型进行设置,包括hex和rgb()。
Note
列标尺始终绘制在列间距(装订线)的正中央。
列规则(见图 4-6 )呈现为好像该列完全被内容填充,不管这是否是真的。如果样式表定义要求平衡填充列以外的内容,这很有用,因为这有助于直观地定义列所占的空间,即使它没有内容。回想一下,使用浮动元素生成列来实现这个简单的效果是多么困难!

图 4-6。
The column rule in action
分栏符
通常,浏览器的渲染引擎决定如何以及在哪里在一个multicol元素中跨列分解内容。这在许多情况下都很有效,但是有时您可能希望控制内容的分割方式。例如,您可能想要强制内容在每个<h3>标签之前的新列中开始呈现。有三个属性可用于控制如何以及何时中断内容:break-before、break-after和break-inside。
先断后合
此属性指定应该在应用元素之前应用分隔符。它接受以下值:auto|always|avoid|left|right|page|column|avoid-page|avoid-column。在 CSS3 多列布局的情况下,特别有趣的是column和avoid-column选项,它们确保或防止(在可能的情况下)在multicol元素中出现中断。
中断后
此属性指定应该在应用了分隔符的元素之后应用分隔符。它接受以下值:auto|always|avoid|left|right|page|column|avoid-page|avoid-column。在 CSS3 多列布局的情况下,特别有趣的是column和avoid-column选项,它们确保或防止(在可能的情况下)在multicol元素中出现中断。
闯入内部
与其他两个选项不同,break-inside控制内容在它所应用到的元素内部如何断开。它接受以下值:auto | always | avoid | avoid-page | avoid-column。在 CSS3 多栏布局的情况下,特别有趣的是column和avoid-column选项,它们确保或防止(在可能的情况下)一个multicol元素的中断。
Note
当分栏符拆分一个框时,该框的边距、边框和填充对拆分位置没有视觉效果。但是,分割后的利润立即生效。
列跨度
column-span属性允许列中的块级元素超出该列的边界。将来可能会指定要跨越的列数,就像可以应用于表格单元格的colspan属性一样,但是在 CSS3 规范中只有两个可能的值:none和all。
默认情况下,column-span设置为none。这意味着内容在列的约束内呈现。如果它被设置为all,列将在元素被渲染的地方断开。这就产生了一种效果,一个multicol元素可以包含多行列。
图像、响应和列中的裁剪
图像在布局中提出了一个特殊的问题,你不能绝对确定多少空间将被分配给它们的显示。有两种方法可以处理分栏布局中的图像:
- 用图像填充列宽,放大或缩小图像以匹配可用空间。
- 预先确定图像应该显示的大小,并根据需要环绕或裁剪图像以填充空间。
清单 4-1 中的例子,如图 4-3 、 4-4 和 4-5 所示,使用第一个选项。使用宽度为 100%的 CSS 规则会强制图像以整列宽度呈现。这有效地提高了图像的响应性,因为它可以根据可用空间的大小进行缩放。
另一种方法是以设定的大小呈现图像。就像任何其他块级容器一样,浮动元素在multicol元素中工作,但是被限制在呈现它们的列中的空间内。此外,如果呈现的图像占据的水平空间比列中可用的空间多,则图像会在该列的有效内容框的边缘被裁剪。这两种不同的图像渲染方法的区别如图 4-7 所示。

图 4-7。
Image 1 is set to 100% width, which allows it to scale to fit the column width. Image 2 uses an absolute size, forcing it to crop to the column width when the column is narrower than the image Note
如果您希望您的图像具有响应性,请使用第一种方法,因为它使图像能够根据可用空间调整大小。
控制如何用内容填充列
使用 CSS3 多列布局,可以通过两种方式在列中填充内容:可以让内容在列之间平衡,也可以按顺序填充,直到内容用完。如果使用后一个选项,有可能会呈现一个或多个没有任何内容的列。
属性提供了对浏览器使用哪种方法的控制。它接受两个可能的值:balance和auto。正如您所料,balance在所有可用的列中平等地呈现内容,auto使用顺序方法。缺省(未指定)值是balance,所以如果您没有显式地将column-fill设置为auto,浏览器会尝试呈现平衡的列填充。清单 4-6 中的代码和图 4-8 中的浏览器内结果显示了使用两个选项呈现的相同内容。

图 4-8。
The two different column-fill options. The first balances the content equally across the columns, and the latter fills columns sequentially
<style>
.multicol1 {
columns: 3 20em;
column-fill: balance;
}
.multicol2 {
columns: 3 20em;
column-fill: auto;
}
</style>
<div class="multicol1">
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis risus eget urna mollis ornare vel eu leo. Nulla vitae elit libero, a pharetra augue. Sed posuere consectetur est at lobortis.</p>
<p>....</p>
</div>
<div class="multicol2">
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis risus eget urna mollis ornare vel eu leo. Nulla vitae elit libero, a pharetra augue. Sed posuere consectetur est at lobortis.</p>
<p>....</p>
</div>
Listing 4-6.Two Different Column-Fill Approaches
控制列内容溢出的方式
正如您看到的超出列边界的图像,延伸到列间隙的内容在列间隙的中间被剪切。再看一下图 4-7 中的图表,看看它是如何工作的。
当内容超出了为一个multicol元素分配的空间时,浏览器可以根据你在 CSS 代码中使用的设置以几种不同的方式呈现。有时这可能会导致在multicol块之外呈现额外的列。理解这一点的最好方法是使用两个简单的例子。
清单 4-7 中的代码创建了一个简单的有三列的multicol元素,并在每个段落后填充包含强制分栏符的内容。当您在 HTML 代码中添加第四个段落时,浏览器会继续执行强制换行,并创建第四列来容纳附加的段落。这被呈现在multicol元素的内容框之外,因为有足够的空间来容纳指定的三列,并且你显式地声明了一个列宽(见图 4-9 )。

图 4-9。
If you force a break after the end of each paragraph, adding a fourth paragraph forces an additional column to render outside the content box
<style>
.multicol {
width: 60em;
columns: 3 18em;
}
p {
break-after: column;
}
</style>
<div class="multicol">
<p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis risus eget urna mollis ornare vel eu leo. Nulla vitae elit libero, a pharetra augue. Sed posuere consectetur est at lobortis.</p>
<p>Donec id elit non mi porta gravida at eget metus. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
<p>Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Curabitur blandit tempus porttitor. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</p>
<p>Maecenas sed diam eget risus varius blandit sit amet non magna. Vestibulum id ligula porta felis euismod semper. Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p>
</div>
Listing 4-7.Example of an Extra Column Rendering Outside the Content Box
在图 4-9 中,内容被强制插入到内嵌方向的附加列中。如果为multicol元素显式指定了高度,并且要呈现的内容量超过了可用空间,也会出现相同的效果。
与任何块级元素一样,如果您为overflow属性指定了一个值,它就会被执行,从而能够控制是否在文档中呈现额外的列。避免生成额外列的最简单方法是不指定multicol元素的高度,并避免强制分栏。
对于分页媒体(如打印页面)如何处理溢出的内容,也有特殊的规则。阅读 W3C 规范以获得完整的细节。
如何使用 CSS 多栏布局
正如您所看到的,CSS 多列布局提供了一个明智而实用的解决方案,可以像报纸一样将内容排列成列。也许该模块最大的好处是它给网页设计过程带来的工作流程的改进,使填补和变通成为过去。此外,该模块在默认情况下具有响应能力,为移动优先的设计方法提供了一个真正有用的选项,同时减少了在所有屏幕分辨率下实现有效解决方案所需的开发时间。
CSS 多列布局是一个很棒的整体布局工具,但它也可以用来创建更小的页面内用户界面元素,例如:
- 内容在大屏幕上排列成栏,但在移动设备上退回到单列的表单
- 水平导航菜单,其中每个元素占据相同的水平空间是很重要的(尽管在 Flexbox 上查看第五章可以获得更好的选择!)
- 在您事先不知道有多少内容或水平空间可用的区域呈现内容
也就是说,CSS 多栏布局的主要目的是作为一个整体的页面布局工具,这是你可以从中获得最大好处的地方。
浏览器支持
浏览器对 CSS 多栏布局的支持已经很不错了。每个主流浏览器都支持至少两个版本。Internet Explorer 目前拥有最完整的实现,从版本 10 开始,当前版本提供对该规范的完全支持和部分支持。Firefox、Chrome、Safari 和 Opera 也都提供了对该规范的部分支持,而基于 WebKit 的浏览器提供了完全支持。在移动平台上,情况是相似的:所有常见的浏览器都有很好的支持,IE mobile 提供了最完整的实现。
CSS 多列布局模块在 2011 年 4 月达到候选推荐状态,这实际上意味着它是一个完整的模块,你可以认为它是稳定的。尽管如此,CSSWG 仍在继续努力完善和进一步开发该模块。您可以在 http://caniuse.com/#feat=multicolumn .查看最新的浏览器对 CSS 多栏布局的支持
Caution
尽管浏览器对该模块的支持非常好,但该标准的旧实现可能包含错误或缺陷。与任何新模块一样,为了确保最佳结果,您必须在所有可能的浏览器配置中进行测试。不要假设每个浏览器都会相同地呈现你的multicol元素。
后备选项和聚合填充
不理解 CSS 多栏布局属性的浏览器会对每个元素使用默认值。这意味着在大多数情况下,设计为具有多列的元素会在单个列中呈现。对于许多布局来说,这并不是什么大问题,而且对于布局的渐进增强方法来说,这是一个合理的结果。如果你的布局中必须有列,不管浏览器是否支持 CSS 多列布局,使用 Modernizr 框架将允许你识别和处理不能显示 CSS 列的浏览器。您可以在 http://modernizr.com .了解更多信息
真实世界的例子
尽管多栏布局并不是什么新东西,但新模块带来的工作流程改进对网页设计者来说意义重大。因此,很值得探索一个真实世界的例子,看看一切是如何组合在一起的。你不会被一个全新的视觉范例所震撼,但是如果你在网络上工作过一段时间,你会欣赏代码的美丽!
模型
对于这个例子,我为一份虚构的在线报纸创建了一个页面设计模型。渲染输出如图 4-10 所示。本示例使用 CSS 多列布局模块呈现设计中显示的所有列,包括布局顶部的嵌套列,其中一行有三个标注。它还使用了本章中讨论的一些特性,包括column-span属性来创建有效的引用。

图 4-10。
A mockup for a fictional online newspaper
HTML 标记
这个页面的 HTML 标记非常简单。其核心是一组元素,每个元素包含一篇文章。无论用户的浏览器是否支持 CSS 多列布局,标记都可以工作。清单 4-8 显示了整个页面的 HTML 代码。
<div id="container">
<header>
<hgroup>
<h1><span>the</span>news</h1>
<h2>Your daily news</h2>
</hgroup>
<nav id="primarynav">
<ul>
<li>News</li>
<li>Sport</li>
<li>Culture</li>
<li>The Arts</li>
<li>Business</li>
<li>Economy</li>
<li>Lifestyle</li>
<li>Travel</li>
<li>Technology</li>
<li>Comment</li>
</ul>
</nav>
</header>
<section id="news">
<nav id="sectionnav">
<ul>
<li>News</li>
<li>US & Canada</li>
<li>World</li>
<li>Politics</li>
<li>Media</li>
<li>Education</li>
<li>Science</li>
<li>Entertainment</li>
<li>Weather</li>
</ul>
</nav>
<div id="pagelayout">
<div id="mainlayout">
<div id="callouts">
<div class="callout1">
<img src="img/callout1.jpg" />
<h2>Banks in Recovery</h2>
</div>
<div class="callout2">
<img src="img/callout2.jpg" />
<h2>Weekend ideas</h2>
</div>
<div class="callout3">
<img src="img/callout3.jpg" />
<h2>New museum set to open</h2>
</div>
</div> <!-- End Callouts -->
<article>
<h2>Island boat service due to stop in September</h2>
<figure>
<img src="img/article.jpg" />
<figcaption>Above: The service provides a lifeline to the local island community</figcaption>
</figure>
<p>Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus, ....</p>
<h3>Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.</h3>
<p>Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus....</p>
</article>
</div> <!-- End Main Layout -->
<aside>
<div id="promoted">
<article>
<img src="img/promoted1.jpg" />
<h3>Jobs of the future</h3>
<p>Susan Hill explains why the jobs market our children will face bears no relation to today's workplace</p>
</article>
<article>
<img src="img/promoted2.jpg" />
<h3>10 amazing getaways</h3>
<p>Our top pick of summer breaks on a budget, that offer a lot of bang for your bucks</p>
</article>
</div> <!-- End Promoted -->
<div id="features">
<h4>Features</h4>
<ul>
<li>
<img src="img/featured1.jpg" />
<h5>Rise of the cupcake</h5>
<p>Inceptos Aenean Dolor Sit Commodo</p>
</li>
<li>...</li>
</ul>
</div> <!-- End Features -->
</aside>
</div> <!-- End Page Layout -->
</section>
</div>
Listing 4-8.HTML Code for the CSS Multi-column Layout Page
呈现列
仅使用 CSS2.1 肯定可以实现这种布局,但是新的 CSS 多列布局模块使得维护页面上的内容更加容易,而不必调整标记或手动对内容进行分页。我将单独向您展示每一部分,以便您可以看到 CSS 多列布局模块是多么有用。
Note
与许多 CSS3 属性一样,在实现阶段,浏览器供应商为属性添加了前缀。我将向您展示不带前缀的代码,以保持清单的整洁。检查您的目标浏览器,看看您是否需要使用每个属性的供应商前缀版本。
主要文章
主要标题和文章区域遵循贯穿全书的逻辑设计模式。CSS 如清单 4-9 所示。这是实现如图 4-10 所示的柱状布局所需的全部代码。
#mainlayout {
max-width: 560px;
columns: 2 250px;
column-rule: 1px solid #999;
column-gap: 20px;
}
#mainlayout article p {
font-size: 1.1em;
line-height: 1.7em;
}
#mainlayout article figure {
width: 100%;
margin: 0;
padding: 0;
}
#mainlayout article figcaption {
font-style: italic;
font-size: 0.8em;
}
#mainlayout article figure img {
width: 100%;
border: 1px solid #297C21;
}
#mainlayout article h2 {
color: #297C21;
column-span: all;
font-size: 2.2em;
font-weight: normal;
}
#mainlayout article h3 {
column-span: all;
padding: 10px 0px;
border-top: 1px solid #999;
border-bottom: 1px solid #999;
font-weight: normal;
color: #333;
font-size: 1.5em;
line-height: 1.5em;
}
Listing 4-9.CSS Code Used to Create the Main Article’s Multi-column Layout
这段代码中的简单规则创建了在主文章区域中呈现两列所需的布局,并打破了 pullquote 的列结构。结果是在 pullquote 的上方和下方呈现一组列行。注意,代码还将column-span: all用于<h2>标记,这将跨两列呈现标题。你可以在图 4-11 中看到输出,这是在 Safari 中渲染的。

图 4-11。
The primary article rendered in Safari
其他列内容
页面上的其他列和内容很容易呈现。使用相同的原则,但值稍有不同,可以呈现顶部的标注部分。因为这个标注区域是主容器的子容器,所以首先需要跨越标注区域的两列,然后为容器设置一个3的column-count,允许三个标注呈现在三列中。HTML 再次显示在清单 4-10 中,相关 CSS 显示在清单 4-11 中,结果呈现在图 4-12 中。

图 4-12。
The three-column callout area sitting above the two-column article, rendered in Safari
<div id="mainlayout">
<div id="callouts">
<div class="callout1">
<img src="img/callout1.jpg" />
<h2>Banks in Recovery</h2>
</div>
<div class="callout2">
<img src="img/callout2.jpg" />
<h2>Weekend ideas</h2>
</div>
<div class="callout3">
<img src="img/callout3.jpg" />
<h2>New museum set to open</h2>
</div>
</div> <!-- End Callouts -->
...
</div>
Listing 4-10.HTML for the Callout Area
#callouts {
column-span: all;
columns: 3 180px;
}
Listing 4-11.CSS Applied to the HTML in Listing 4-10, Spanning the #mainlayout Columns and Setting Up a Three-Column Layout for the Callout Area
剩下的部分是右边较小的列区域。这以类似于主内容区域的方式呈现,但是它包括堆叠列表项和特性。相关的 HTML 代码如清单 4-12 所示。
<aside>
<div id="promoted">
<article>
<img src="img/promoted1.jpg" />
<h3>Jobs of the future</h3>
<p>Susan Hill explains why the jobs market our children will face bears no relation to today's workplace</p>
</article>
<article>
<img src="img/promoted2.jpg" />
<h3>10 amazing getaways</h3>
<p>Our top pick of summer breaks on a budget, that offer a lot of bang for your bucks</p>
</article>
</div> <!-- End Promoted -->
<div id="features">
<h4>Features</h4>
<ul>
<li>
<img src="img/featured1.jpg" />
<h5>Rise of the cupcake</h5>
<p>Inceptos Aenean Dolor Sit Commodo</p>
</li>
<li>...</li>
</ul>
</div> <!-- End Features -->
</aside>
Listing 4-12.
Sidebar Content Area
对于页面的这一部分,CSS 实际上非常简单。唯一需要的 CSS3 多列布局代码强制第二列成为一个新列,并首先创建这两列。重要的位如清单 4-13 所示;结果如图 4-13 所示。

图 4-13。
The resulting layout shows two columns in the sidebar area. Note that I’ve used a combination of image techniques in this layout, which is rendered in Safari
#pagelayout > aside {
max-width: 350px;
columns: 2 160px;
margin-left: 10px;
}
#pagelayout > aside img {
width: 100%;
}
#features {
break-before: column;
}
#features ul li img {
clear: left;
width: 35%;
float: left;
}
Listing 4-13.Pertinent Bits of CSS Used to Achieve the Two-Column Layout in the Sidebar
记住 CSS3 多栏布局是默认响应的。这意味着随着可用水平呈现空间的减少,主布局中呈现的列数也会减少。回想一下,在主文章区域中显示了一组嵌套的列。因为您没有为这些列指定最大或固定的宽度,所以它们会收缩,直到达到 220px 的最小宽度。一旦它们达到那个宽度,它们就不能再变小了;相反,浏览器在单个列中呈现内容,该列可以扩展以填充可用空间。你可以在图 4-14 中看到这个效果。

图 4-14。
Notice that when the browser window is pulled in to be narrower than a typical desktop resolution, the content automatically reformats to reduce the number of columns
当以智能手机分辨率查看内容时,也会发生同样的响应性重新格式化。您可以通过将浏览器窗口拖至尽可能小的宽度来模拟这种效果。在这种情况下,一旦窗口窄于 500px,内容将再次重新格式化,在一个连续的列中显示所有主要内容,如图 4-15 所示。

图 4-15。
On a smartphone, the page automatically responds, rendering content in a single column layout rather than in columns side by side Note
需要一些额外的非 CSS 多栏布局 CSS 代码来定义颜色、边框和印刷样式,如图 4-10 到 4-15 所示。
摘要
CSS 多列布局提供了一种非常有用的方法,可以将内容呈现为好看且易于维护的读者友好的列。这是新模块的主要优势,因为它简化了这些复杂布局的创建,使未来更新内容的速度大大加快,并且首先需要更少的黑客和变通方法来创建布局。
浏览器支持非常好,所以这是在网络上使用的所有新 CSS3 布局模块中最安全的一个。不支持的浏览器会自动退回到单列布局。通过为各个列使用指定的宽度,布局在默认情况下也会做出响应,自动重新格式化以适应更小或更大的屏幕,而无需任何进一步的代码。
使用像 Modernizr 这样的库,可以很容易地为不兼容的浏览器提供一组使用旧的 CSS 2.1 规范的多填充样式。但是在很多情况下,这并不是绝对必要的,因为大多数布局都没有分栏来分隔内容。
五、CSS 灵活的盒子布局
CSS 灵活的盒子布局模块解决了一个定位问题,这个问题是 web 设计者从 CSS 诞生的第一天起就一直在努力解决的:沿水平或垂直轴均匀地间隔元素,而不需要求助于复杂的浮动或基于脚本的技巧。这一章将更详细地介绍灵活的盒子布局模块,并展示它如何革新你设计网页的方式。
Note
该模块通常被称为 Flexbox,因此在本章中,我交替使用术语 Flexbox Layout 和 Flexbox 来指代该模块。
什么是 Flexbox?
CSS 灵活的盒子布局提供了一个为用户界面设计优化的盒子模型。使用 flex 布局模型,启用 flex 的容器的子元素可以被布置在轴上(水平或垂直),并且这些子元素可以自动增长和收缩以填充可用空间,而不会溢出父容器。Flexbox 模块的一个特别的好处是易于设置和操作子元素的对齐。这使得定位内容变得简单,同时保留了以后引入其他兄弟元素的灵活性:使用基于像素的布局的日子已经一去不复返了。
也可以将单个 flex 容器嵌套在其他 flex 容器中。通过将垂直容器嵌套在水平容器中,您可以构建灵活的跨两个轴的布局,反之亦然。
CSS 2.1 引入并定义了以下四种用于呈现网页的布局模式:
- 文档的块布局
- 文本的嵌入式布局
- 二维网格中表格数据的表格布局
- 定位布局,用于在页面上显式定位元素,并将其从文档流中移除
浏览器在解析 CSS 规则时使用这些系统。这些布局模式根据元素的同级、文档中的流动和父元素来确定元素的大小和位置。
Flexbox 引入了第五种布局模式,W3C 将其命名为 flex layout(见图 5-1 )。这种模式是专门为排列复杂的用户界面元素而设计的。Flex-layout 模式考虑了比 CSS2.1 更复杂的页面和 web 应用程序的使用场景。

图 5-1。
Flexbox in action, rendering a page without the need to use workarounds to achieve the layout. You build this layout at the end of this chapter
语法和结构
W3C CSSWG 自 2009 年第一份工作草案发布以来一直致力于 Flexbox 模块。在此期间,规范和语法发生了实质性的变化。目前的规范包括 2012 年 9 月发布的候选人推荐和 2013 年 10 月更新的编辑草案。
一个候选推荐被认为是稳定的,并且这一个形成了本章中详细描述的语法的基础。虽然没有太大的不同,但编辑的草稿引入了一些我也提到的改进。我会指出,这里显示的语法来自编辑的草稿,而不是候选人的推荐。此外,我会让您知道浏览器目前提供的支持。
Note
截至 2013 年 10 月 3 日,Flexbox 模块的最新版本是基于 W3C 候选推荐标准的编辑草案。编辑的草稿是公开讨论的,可以随着时间的推移而改变。因此,语法不能被认为是稳定的。为了避免问题,请确保您使用的是最新的候选人推荐。您可以在 http://dev.w3.org/csswg/css-flexbox/ 查看当前编辑的规范草案。
基本概念和术语
在对 Flexbox 的介绍中,W3C 将 flex 布局描述为表面上与块布局相似,因为它们遵循相似的设计模式。我发现把 flex layout 想象成有点像 table layout 是有帮助的,因为它允许元素相对于轴对齐和调整大小,就像表格的单元格被挤压成一行一样。正如您将看到的,flex 布局实际上非常不同,尽管它确实具有块、内联和表格布局的可识别特性。
本质上,flex 布局很简单。但是不要被骗了,以为 Flexbox 功能没那么强大,因为它很容易上手。这是一个非常通用的布局模块,允许您执行以下任务:
- 以四种不同方向之一布局元素:从左到右、从右到左、从上到下或从下到上
- 仅使用 CSS 重新排列元素的顺序
- 自动调整元素大小以适应可用空间
- 根据容器或同级元素对齐元素,实现通用的跨轴比例
- 折叠容器内的元素,而不影响容器的偏移量
- 创建线性单轴布局或沿横轴缠绕的多线布局
灵活布局模式的核心是轴的概念。Flexbox 是一个二维布局工具,有两个可能的工作轴:水平,简称row;还有垂直的,简称column。
Flexbox 布局需要一个元素作为 flex 容器,以及零个或多个作为 flex 项目的子元素。这些 flex 项目使用 flex 布局模型进行布局,而父容器可以应用其他布局模型(如 float)。这意味着您可以将 flex 容器合并到您的标准 CSS 2.1 布局中,而不必改变整个结构方法。
浏览器支持
浏览器支持灵活的盒子布局已经很不错了。Internet Explorer 从版本 10 开始就部分支持该规范,IE11 中包含了完全支持。Firefox、Chrome、Safari 和 Opera 都至少部分支持该规范,基于 WebKit 的浏览器提供完全支持。
在移动平台上,iOS Safari 有很好的支持,黑莓的浏览器也是如此。IE mobile 和 Android 的浏览器都有部分支持。只有 Opera Mini 目前不支持该规范。您可以在 http://caniuse.com/#search=flex 查看 Flexbox 最新的浏览器支持。
Caution
在灵活盒子布局模块的开发过程中,一些浏览器实现了该规范的早期版本。语法已经改变了。因此,Web 上的许多示例现在都已经过时了,所以在遵循示例代码时要小心,以确保您使用的是模块的正确版本。
不理解 Flexbox CSS 属性的浏览器会对每个元素使用默认值。通常情况下,<div>、<section>和<article>等结构项显示为块级元素,而<span>等内联元素则恢复为默认的内联级行为。您可以利用这些信息来处理较旧的浏览器;或者,如果您想填充类似 Flexbox 的渲染,Modernizr JavaScript 库允许您测试浏览器支持。您可以在 http://modernizr.com 了解更多信息。
方向和大小
要完全理解 Flexbox 容器的流程方向的含义以及起点和终点在哪里,使用通用语言定义 Flexbox 模块的相关方面会有所帮助。W3C 在灵活盒子布局的候选推荐草案中已经在这方面做了很好的尝试,所以我在讨论方向和大小时使用了相同的方法。
flex 容器的主轴是沿其放置各个 flex 项目的轴。主开始和主结束是根据页面的语言定义的,并与主尺寸的开始和结束对齐。横轴垂直于主轴延伸,并具有十字起点和十字终点。同样,这些是由交叉大小定义的。
图 5-2 展示了我在讨论 Flexbox 时使用的不同轴和命名约定。你可能想回头参考这个图,直到你对两个轴的概念感到舒服为止。

图 5-2。
Terms used when talking about Flexbox dimensions and flow direction
举个例子会让这个理论更容易理解。清单 5-1 显示了这个例子的代码。你可以在图 5-3 中看到样本输出。我将在接下来的几页中解释这是怎么回事。

图 5-3。
The result of combining the HTML and CSS code shown in Listing 5-1
<section id=library">
<article class="library-item">
<h1>The Wonderful Wizard of Oz</h1>
<h2>by L. Frank Baum</h2>
<img src="img/oz.jpg" alt="Original cover of The Wonderful Wizard of Oz" />
<button>Remove from library</button>
</article>
<article class="library-item">
<h1>Pride and Prejudice</h1>
<h2>by Jane Austen</h2>
<img src="img/pandp.jpg" alt="Pride and Prejudice book cover" />
<button>Remove from library</button>
</article>
<article class="library-item">
<h1>Adventures of Huckleberry Finn</h1>
<h2>by Mark Twain</h2>
<img src="img/huck.jpg" alt="Original cover of Huckleberry Finn" />
<button>Remove from library</button>
</article>
</section>
<style>
#library {
position: relative;
display: flex;
flex-flow: row wrap;
}
.library-item {
display: flex;
flex-flow: column;
}
.library-item > img {
order: -1;
align-self: center;
}
.library-item > button {
margin-top: auto;
}
</style>
Listing 5-1.HTML Markup Being Styled with Flexbox CSS Properties
Note
需要一些额外的非 Flexbox 代码来定义图 5-3 中所示的颜色、边框和印刷样式。
让我们来看看这个例子的不同柠檬。
Flex 容器
flex 容器是根据 flex 布局的规则和属性放置 flex 项目的元素。Flex 项目是 flex 容器的直接子对象。每个 flex 容器可以包含零个或多个 flex 项目,这些项目可以是显式元素,如<div>、<img>、<section>和<article>,也可以是一串连续的文本,Flexbox 将其视为包含在一个元素中。
通过使用设置为值flex或inline-flex的display属性,将元素定义为 flex 容器。display: flex和display: inline-flex的区别在于display: flex将容器定义为 CSS 2.1 块级项目,而display: inline-flex将容器设置为内联级元素。清单 5-2 显示了将#library部分定义为具有自己的 flex 上下文的 flex 容器的代码。
#library {
display: flex;
}
Listing 5-2.Defining the Library Element as a Flex Container by Assigning display: flex;
当您使用display: flex或display: inline-flex定义一个 flex 容器时,您正在为该容器的内容创建一个新的 flex 格式上下文。该上下文仅影响该容器的 flex item 子元素。外部元素不会影响嵌套的弹性项目。在flex的渲染中缺乏对外部元素的感知意味着float和clear对 flex 项目没有影响。还值得注意的是,多列布局模块中的列属性对 flex 项目没有影响。
Flex 格式上下文
每个 flex 格式上下文都独立于其在布局中的对应部分工作。因此,对于创建完美的网格来说,Flexbox 并不是一个好的选择,在本书的后面,当我讨论一个专门为网格布局设计的模块时,你会看到这一点。这是因为单个容器允许它们的 flex 项目根据它们的大小和内容扩展或收缩,而不引用相邻 flex 容器中的 flex 项目。相比之下,基于网格的布局严格遵循特定的节拍和节奏,并考虑到相邻的元素。图 5-4 展示了三种不同的 flex 容器,每种容器都有自己的 flex 格式上下文,不关注相邻的容器。

图 5-4。
Flex items are sized and positioned relative to their own flex container, not flex items inside other flex containers
展示模型
像 flex 容器一样,flex 项目定义了自己的格式上下文。这可以使用display属性和任何 CSS 2.1 允许的值来设置。因此,您可以将伸缩项设置为float,显示inline,或者设置为table-cell。如果使用显示值flex,flex 项本身就变成了一个 flex 容器,并支持其他属性,如visibility: collapse,这将在本章后面讨论。
Note
如果您使用position: absolute或position: fixed,flex 项将从 flex 格式上下文流中取出,除非left和right或top和bottom都设置了值auto。在这种情况下,这些位置属性的值是根据 flex 容器上下文中 flex 项目的静态位置计算的。
在下一个例子中,主 flex 容器是一个<section>元素,其id为library。每个子元素——一个具有library-item类的<article>元素——也被设置为使用display: flex。清单 5-3 隔离相关的 CSS。
.library-item {
display: flex;
flex-flow: column;
}
Listing 5-3.CSS to Define Each Flex Item in the Context of the #library Flex Container
每个 flex 项目实际上都是一个 flex 容器,如图 5-5 所示。因此,每个条目的内容也被视为 flex 条目,但仅在父条目library-item的上下文中。

图 5-5。
Each flex item in the #library flex container acts as its own flex container, following a horizontal flex direction
弯曲方向
flex-direction用于定义一个柔性容器的主轴。回想一下,灵活盒子布局是一个二维布局模块,因此只有两个轴可供选择:
row:英文横。
column:英文竖着。
每个轴可以向前或向后运行。定义flex-direction: row反向版本的语法是flex-direction: row-reverse;而对于flex-direction: column,是flex-direction: column-reverse。
Caution
术语row和column可能会让你分别想到水平和垂直布局,但在 Flexbox 中,这只适用于水平书写模式。在垂直语言中,如日语,row 从上到下排列内容。
默认主轴以及flex-direction值是根据网页上使用的书写模式设置的。对于英语来说,这是从左到右,从上到下,定义为ltr。结果是使用ltr书写模式创建的网页默认为row,并且从左到右运行。设置值row-reverse使伸缩项从右向左显示。
柔性包装
属性控制 flex 容器是单行的还是多行的。有三种可能的值:
nowrap:将 flex 容器定义为单行。容器中的所有 flex 项目都适合一个线性运行,而不需要换行到两行或更多行。这是flex-wrap的默认值。
wrap:允许伸缩项沿横轴分布在两条或多条线上。
wrap-reverse:类似wrap工作,但运行方向与默认相反。
如果通过使用值wrap或wrap-reverse允许 flex 容器跨多行呈现,当没有足够的空间在一行中显示所有项目时,flex 项目会换行到第二行(或第三、第四或第五行)。对于响应式设计来说,这是一个非常有用的选项,因为它会根据容器自动对内容重新分页。
与flex-direction一样,默认的flex-wrap方向由写入模式定义。对于英语,这是从左到右,从上到下。在某些语言中,可能是从右到左,从上到下,或从上到下,从左到右。为了避免疑问,使用 HTML 元素上的lang和dir属性显式设置页面的语言和书写模式,如下所示:
<html lang="en" dir="ltr">
柔性流
flex-flow为flex-direction和flex-wrap属性提供了方便的快捷方式。它允许您使用一行代码来定义这两个属性,但是它也可以在没有flex-wrap值的情况下使用,就像flex-direction一样工作。清单 5-4 使用了flex-flow,尽管因为nowrap是flex-wrap的默认值,我可以省略第二个值。最终布局如图 5-6 左侧所示;右侧显示了如果我指定了wrap的话布局会是什么样子。

图 5-6。
Notice the difference between the nowrap and wrap values for flow-wrap
.library-item {
display: flex;
flex-flow: column nowrap; /* nowrap is the default value for flex-wrap, so isn't strictly necessary here */
}
Listing 5-4.
Flex-flow Shorthand Solution for Defining Both the flex-direction and flex-wrap Properties
控制项目的顺序
Flexbox 最好的特性之一是能够使用纯 CSS 控制 flex 项目的顺序。这是允许网页设计者正确区分风格和结构的一个重大进步,也给搜索引擎优化带来了好处。
默认情况下,伸缩项沿主轴排列。通过使用order属性,您可以覆盖缺省值并指定特定项目在流中的位置。Order取整数值,较低的值呈现在较高的值之前。负值也是允许的,如清单 5-5 所示。
.library-item > img {
order: -1;
align-self: center;
}
Listing 5-5.Negative Value for the order Property, Forcing the Image to Render at the Beginning of the Flow Along the Main Axisy
让我们修改这个 CSS 代码来看看order属性的作用。清单 5-6 用粗体显示了变化。图 5-7 按照单个元素的顺序显示了结果。

图 5-7。
By adding specific order property values to each library-item, you can reorder them along the main axis without having to change the markup
<style>
#library {
position: relative;
display: flex;
flex-flow: row wrap;
}
.library-item {
display: flex;
flex-flow: column;
}
.library-item:nth-child(1) {
order: 3;
}
.library-item:nth-child(2) {
order: 1;
}
.library-item:nth-child(3) {
order: 2;
}
.library-item > img {
order: -1;
align-self: center;
}
.library-item > button {
margin-top: auto;
}
</style>
Listing 5-6.CSS Applied to the HTML Code in Listing 5-1, Updated to Include order Properties for the library-item Elements
Note
order属性只影响可视媒体,因此屏幕阅读器继续按照内容在标记中出现的顺序来阅读内容。在检查页面布局的可访问性时,理解这一点很重要。
控制柔性
也许 Flexbox 规范中最重要的部分是定义 flex 项目的大小和间距的能力。传统上,很难在一个轴上排列导航项目,以使它们以相等的间距扩展或收缩来适应主尺寸。Flexbox 通过以下flex-属性提供对这两个设计方面的完全控制:
flex-grow
flex-shrink
flex-basis
这三个属性的组合可以很好地控制一个项目在没有足够的空间时是增长以填充空间还是收缩以适应空间,这是增长和收缩的基础。您马上会看到一个示例,但是让我们先依次看一下每个属性。
灵活增长
flex-grow属性定义了相对于同一 flex 容器上下文中的其他 flex 项,flex 项增长了多少。该值被指定为整数,默认为 1。因为该属性与上下文中的其他项目相关,如果您为一个特定项目设置值 2,则任何额外的空间都将被分割,这样,对于分配给其他 flex 项目的每 10 个像素的额外空间,flex-grow值为 2 的项目将获得 20px 的额外空间。属性flex-grow的值0防止任何可用的额外空间被给予灵活项目。
弯曲收缩
flex-shrink类似于flex-grow,但是决定了当 flex 项目收缩以适合 flex 容器时如何分配空间。同样,该值是一个整数,默认值为 1,并且相对于容器中的其他 flex 项。当缺少可用空间时,flex-shrink属性的值0防止伸缩项收缩。
弹性基础
flex-basis设置 flex 项目的初始宽度,但也可以设置为值auto,这允许浏览器根据项目的内容计算宽度。当flex-basis设置为正值或auto时,flex-grow和flex-shrink的基准都设置为内容周围的间距。(参见图 5-8 。)当flex-basis的值设置为0时,flex-shrink和flex-grow对项目占用的总空间进行操作。(参见图 5-9 。)

图 5-9。
When flex-basis is set with a value of auto or a width greater than 0, only the extra space is distributed

图 5-8。
When flex-basis is set with a value of 0, all the space is evenly distributed
flex 速记属性及其特例
您可以使用flex属性作为flex-grow、flex-shrink和flex-basis的简写。对于flex有一些关于默认值和省略值的特殊规则,以说明 W3C 期望设计者使用该属性的最常见方式。这些很快就会显示出来以供快速参考,但是如果您有任何疑问,请确保在使用flex属性时指定这三个值中的每一个。
清单 5-7 显示了flex属性的语法。属性设置等同于列表后显示的单个属性。
// Syntax flex: <flex-grow> <flex-shrink> <flex-basis>
flex: 1 1 auto;
// This is short-hand for each of following property:value pairs
flex-grow: 1;
flex-shrink: 1;
flex-basis: auto;
Listing 5-7.
flex-grow, flex-shrink, flex-basis, and the Shorthand flex Properties
三个flex-属性的组合有几种常见的使用场景,因此规范定义了一些特殊的规则,当省略了flex速记语法的单个元素或者对其应用了文本值时,可以应用这些规则。这些是从 W3C 候选推荐标准中直接分解出来的:
flex: 0 auto或flex: initial:相当于flex: 0 1 auto,也是该属性的默认值。它根据 CSS 中定义的width和height属性来调整 flex 项的大小。如果项目的主轴大小设置为auto,则项目的大小基于其内容。即使 flex 容器中有可用空间,该值也可以防止 flex 项目增长,但当空间不足时,它可以缩小到最小大小。
flex: auto:这个跟flex: 1 1 auto一样。该值根据 CSS 中定义的width和height属性来调整 flex 项目的大小,但允许它完全灵活地扩展和收缩,以适应可用空间的大小。
flex: none:相当于flex: 0 0 auto。当根据 CSS 中定义的width和height属性调整大小时,该值创建一个不灵活的 flex 项目。它类似于flex: initial,但不允许项目收缩。
flex: <positive-number>:这个跟flex: <positive-number> 1 0px一样。结果是一个flex-basis为 0 的灵活伸缩项,根据容器中不同伸缩项上定义的伸缩因子来分配总空闲空间的一部分。
flex 的一个例子
flex属性为整个 Flexbox 模块提供了一个最令人兴奋的设计机会。现在,创建既有响应性又成比例的灵活布局变得很简单。清单 5-8 展示了这种灵活性。(你会在本章末尾看到一个更深入的例子。)
<style>
.layout {
display: flex;
flex-flow: row nowrap;
}
section > aside {
flex: auto;
}
section > article {
flex: 2 1 auto;
}
aside.level1 {
order: -1;
}
aside.level2 {
order: 2;
}
</style>
<section class="layout">
<article class="maincontent">
...
</article>
<aside class="level1">...</aside>
<aside class="level2">...</aside>
</section>
Listing 5-8.HTML Markup and CSS to Which the Flexbox Properties Are Being Applied
应用于清单 5-8 中标记的 CSS 将主要内容<article>设置为占据两倍于<aside>元素的空间。所有的元素都会根据容器的大小而增减,内容也会重新排序,形成一个典型的三列布局。这种常见的布局方式如图 5-10 所示;在 Flexbox 之前,它需要浮动元素和特定的 HTML 顺序。

图 5-10。
The result of the combination of HTML code and CSS shown in Listing 5-8
控制弹性项目的对齐
扩展和收缩 flex 项目以及沿主轴和横轴对齐和调整其大小的能力是 Flexbox 最有用的方面之一。使用 CSS,您第一次可以准确地定义项目在容器中应该如何对齐,还可以确定项目之间的间距。尽管已经有了脚本解决方法,但直到现在还不可能创建一个灵活的、水平居中的导航栏。更令人印象深刻的是垂直对齐项目(或横轴)的能力。
主轴
在 Flexbox 中有两种不同的方法来控制沿主轴的对齐:通过margin和justify-content属性。
边缘
边距在 flex 项目上的工作方式与 CSS 2.1 边距在块级元素上的工作方式非常相似。如果您设置了自动边距,flex 容器中的任何可用空间都会被分配给该轴上的边距。因此,通过将margin-left: auto指定给一个 flex 项,你将它推到容器的右边(见图 5-11 ),而margin-right: auto将该项推到左边。以这种方式使用 margin 还可以防止项目增长以填充可用空间,因为所有空间都被 margin 占用了。清单 5-9 借用了 W3C 候选推荐规范中的一个例子来说明如何使用边距将一个列表项推到导航栏的右侧。

图 5-11。
This output is achieved with Listing 5-9
<style>
nav > ul {
display: flex;
}
nav > ul > #login {
margin-left: auto;
}
</style>
<nav>
<ul>
<li><a href=/about>About</a>
<li><a href=/projects>Projects</a>
<li><a href=/interact>Interact</a>
<li id='login'><a href=/login>Login</a>
</ul>
</nav>
Listing 5-9.Using margin to Align One Item to the Main End Edge
调整内容
Justify-content控制沿主轴分配给 flex 容器上下文中的 flex 项目的对齐类型。计算完任何一个margin或flex后,应用justify-content属性。这对于指定了最大弹性项目宽度或项目在某种程度上不灵活的布局非常有用。Justify-content分配剩余的可用空间。
justify-content有五种不同的可能值:
flex-start从 flex 容器的主起始边缘对齐项目。如果您的 flex 流从右到左水平运行,这意味着 flex 项目与容器的左侧对齐。
flex-end从柔性容器的主要末端边缘对齐项目。如果您的 flex 流从右到左水平运行,这意味着 flex 项目将与容器的右侧对齐。
center特别令人兴奋,因为它允许项目与 flex 容器的中心对齐,自动考虑所有 flex 项目占据的总主轴空间以及它们之间的任何间距。在灵活的盒子布局出现之前,这是不可能用 CSS 单独实现的。
space-between沿主轴均匀分布柔性项目,第一个项目与主起点对齐,最后一个项目与主终点对齐。注意,如果没有足够的空间容纳所有的 flex 项,这个值的行为与flex-start相同。
space-around与space-between相似,但在第一个项目之前和最后一个项目之后增加了一半大小的空间。剩余的项目再次平均分布在第一个和最后一个项目之间。
图 5-12 展示了这五个选项。

图 5-12。
These alignment options are available with justify-content
在多条生产线上工作
允许 flex 容器换行的一个好处是它给你的布局带来了灵活性。例如,如果你使用 Flexbox 导航条,你可以允许单独的导航选项溢出到第二行,如果有太多的导航选项在一行上放不下的话。这将自动使你的导航栏响应不同的屏幕尺寸。
如图 5-13 所示,最终结果可能有点不美观。但是,正如您所看到的,您可以使用flex属性来使各个导航项目占据空间。如果你给弹性项目添加一个flex: auto规则,最终结果如图 5-14 所示更加漂亮。

图 5-14。
The same navigation bar with flex: auto applied to the flex items

图 5-13。
A navigation bar with flex-wrap: wrap enabled
横轴对齐
除了沿主轴对齐的能力之外,Flexbox 还提供了对横轴上 flex 项目对齐的控制。您不仅可以对齐,还可以自动拉伸项目,使它们在横轴上占据相同的空间,解决了基于浮动的布局多年来一直难以解决的问题。
有三个属性可用于控制横轴对齐:
align-items
align-self
align-content
这些属性中的每一个都行使不同类型的控制,所以让我们依次来看看它们。
对齐项目和对齐自身
align-items和 align-self 的工作方式与justify-content类似,但它们是沿着横轴而不是主轴工作的。align-items适用于 flex 容器,而您可以在单个 flex 项目上使用 align-self。与justify-content一样,这些属性是在应用任何余量后计算的。
以下是align-items和align-self的可能值:
flex-start和flex-end的工作方式与您预期的完全一样,分别将项目对齐十字起点或十字终点边缘。
center根据 flex 容器沿横轴的总尺寸,沿横轴中心对齐项目。如果容器被设置为使用wrap或wrap-reverse的flex-flow值,允许多行,则项目沿着它们出现的行的中心对齐。
baseline将每个项目的基线与十字起始边缘对齐。
stretch沿横轴扩展项目以填充线条。这具有使每个伸缩项沿横轴占据相同空间的效果。如果您的 flex 项目设置了min-height/min-width或max-height/max-width,这些值仍然适用于这些项目,可能会导致项目无法填充行或溢出行。stretch是align-items的默认值。
Auto是align-self的默认值。
图 5-15 显示了所有不同的选项和最终布局。

图 5-15。
These are the alignment options for align-items and align-self
对齐内容
align-content 属性的工作方式与justify-content类似,但它作用于启用了wrap或wrap-reverse的 flex 容器中的行,决定如何在这些行之间分配额外的空间。与justify-content一样,有几种不同的可能值,如图 5-16 所示:

图 5-16。
Different align-content options are available for distributing and aligning individual lines in the flex container
flex-start从柔性容器的十字起始边缘对齐线条。如果您的 flex 流从右向左水平延伸,这意味着线条与容器顶部对齐。
flex-end从柔性容器的交叉端边缘对齐项目。如果您的 flex 流从右向左水平延伸,这意味着线条与容器底部对齐。
center将所有行对齐横轴上 flex 容器的中心,自动考虑所有行占据的总空间以及它们之间的任何间距。
space-between沿十字轴均匀分布线条,第一条线条对齐十字线起点,最后一条线条对齐十字线终点边缘。注意,如果没有足够的空间容纳所有的行,这个值的行为与flex-start相同。
space-around在第一行之前和最后一行之后添加半个空格。剩余的线再次平均分布在第一和最后一行之间。space-around类似于justify-content。
- 拉伸会导致线条自动拉伸,并填充任何额外的可用空间。如果没有足够的空间来容纳所有的行,这个值同样呈现给
flex-start。
Note
align-content仅适用于具有多行的柔性容器。这是因为单行容器的行自动填充分配给容器的整个空间。
折叠的项目
您可以通过为visibility属性指定值collapse来折叠 flex 项目的可见性。这具有将 flex 项目从页面呈现中移除的效果,同时将它保留在格式化结构中。这允许折叠的项目定义 flex 容器横轴的整体比例,同时从视图中隐藏。
这个新选项对于用户界面元素(如下拉导航菜单)特别有用,在下拉导航菜单中,只有顶级选项才会显示,直到用户选择一个项目,子项才会显示。通过继续影响横轴比例,您可以根据菜单中的最大选项自动设置菜单大小,即使这是折叠的。清单 5-10 完美地展示了这种行为。图 5-17 显示结果。

图 5-17。
The navigation menu with the second option opened to reveal the submenu. The widest submenu item sets the width of the overall menu container, despite being initially hidden
<style>
nav > ul > li {
display: flex;
flex-flow: column;
}
/* dynamically collapse submenus when not targeted */
nav > ul > li:not(:target):not(:hover) > ul {
visibility: collapse;
}
</style>
<nav>
<ul>
<li id="nav-about"><a href="#nav-about">About</a>
...
<li id="nav-projects"><a href="#nav-projects">Projects</a>
<ul>
<li><a href="...">Art</a>
<li><a href="...">Architecture</a>
<li><a href="...">Music</a>
</ul>
<li id="nav-interact"><a href="#nav-interact">Interact</a>
...
</ul>
</nav>
Listing 5-10.Example from the W3C Specification, Showing a Dynamic Menu that Collapses the Visibility of Submenu Items
如何使用灵活的方盒子布局
到目前为止,您应该对 Flexbox 的功能和用途有了清晰的认识。它可以单独解决许多常见的布局问题,因此很容易将 Flexbox 视为所有布局需求的答案。我想阻止你屈服于这种诱惑。尽管 Flexible Box Layout 能够呈现整个页面布局,但其他布局模块是专门为整个页面布局设计的(参见这一章之前和之后的章节,了解一些很好的选项!).
与整体页面布局相比,Flexbox 更适合单个用户界面元素。一些常见的使用场景包括:
- 希望在两个轴上真正居中的元素
- 需要呈现数量未知的项目的场景,例如通过内容管理系统控制的菜单
- 您希望根据标记顺序对内容进行重新排序的页面区域(尽管其他一些布局模块也允许您这样做)
- 隐藏当前未选择的内容的标签和内容群组
- 表单和表单元素布局
当然,没有什么可以阻止你使用 Flexbox 作为你的主要布局工具,但是考虑一下最适合手头任务的布局模块是值得的。
我不能放过一个关于 Flexbox 的章节而不提供一个 Flexbox 的实际例子。下面的示例只是一个可能的使用场景,它结合了您在前面几页中看到的许多属性。如果你还不相信灵活的盒子布局模块的能力和灵活性,我敢肯定这个例子会说服你。
真实世界的例子
这个例子为一个虚构的房地产公司创建了一个页面设计模型,如图 5-18 所示。该示例使用 Flexbox 来呈现几个布局部分。

图 5-18。
A mockup for a fictional real-estate company
HTML 标记
这个页面需要的 HTML 标记是基本的。它遵循的模式类似于布局设计者过去使用浮动来排列设计元素时使用的模式。您可以使用 Flexbox 来创建整个页面,但是因为最好将每个模块用于其预期目的,所以本示例将重点放在页面的以下部分:
- 导航栏
- 大屏幕区域
- 利益陈述
清单 5-11 显示了页面这些部分的相关 HTML 代码。
<!—The navigation section -->
<nav>
<ul>
<li><a href="#">Home</a></li>
<li><a href="#">Locations</a></li>
<li><a href="#">Financing</a></li>
<li><a href="#">Special Offers</a></li>
<li><a href="#">About us</a></li>
<li><a href="#">Contact Us</a></li>
<li class="searchform"><form><input type="text" value="search" /></form></li>
</ul>
</nav>
<!—The big icons/jumbotron section -->
<section id="jumbotron">
<article>
<h2>Free Advice</h2>
<p>All our impartial advice is offered completely free of charge</p>
<img src="img/bigicon-freeadvice.png" />
</article>
<article>
<h2>Discounted Removals</h2>
<p>Once you've found your dream...
...</article>
</section>
<!—The badge benefits section -->
<section id="benefits">
<article>
<h1> Looking for a beautiful new home that won't break the bank?</h1>
<p> Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue. Cras mattis consectetur purus sit amet fermentum.</p>
</article>
<article class="badge">
<div>
<h3>Quality without compromise</h3>
<p>We have homes that suit every budget without compromising on quality</p>
</div>
<img src="img/badge-quality.png" />
</article>
<article class="badge">...
...</article>
</section>
Listing 5-11.HTML Code for Three Parts of the Page Suited to Flexbox Layout
虽然使用 CSS2.1 可以实现这种布局,但使用 Flexbox,在页面上正确排列项目几乎变得微不足道。让我们单独看一下每一部分,这样你就可以看到 Flexbox 的布局是多么强大。
Note
与许多 CSS3 属性一样,在实现阶段,浏览器供应商会给属性添加前缀。我将向您展示不带前缀的代码,以保持清单的整洁。在支持完成之前,您需要使用每个属性的供应商前缀版本来完全支持每个浏览器。
航行
导航区域遵循您在清单 5-9 中看到的相同设计模式,因此这一部分的代码几乎相同。清单 5-12 显示了实现实体模型中显示的导航布局所需的所有代码!
/* The navigation section */
nav > ul {
display: flex;
flex-flow: row wrap;
}
nav > ul > .searchform {
margin-left: auto;
}
Listing 5-12.Flexbox CSS Code to Create the Navigation Layout
这段代码中的两个简单规则创建了将搜索表单推到导航区域右侧所需的布局。你可以看到图 5-19 中的输出,它是在 Chrome 30 中渲染的。

图 5-19。
The navigation output in Chrome
大屏幕
大屏幕区域(带有大图标)也很容易成形。Flexbox 可以帮助解决该领域正在进行的几件事情,例如:
- 每个
<article>大小均匀。
- 大屏幕中每个元素的内容在两个轴上居中对齐。
- 图像在文本之前呈现,但出现在标记中的文本之后。
为了对这些内容重新排序,您需要将每篇文章呈现为整个 flex 容器中的一个 flex 容器,id 为#jumbotron。参见清单 5-13 。
/* The jumbotron section */
#jumbotron {
display: flex;
flex-flow: row wrap;
align-content: stretch;
justify-content: center;
}
#jumobtron article {
display: flex;
flex-flow: column nowrap;
flex: 1 1 250px;
align-content: center;
justify-content: center;
}
#jumbotron article * {
align-self: center;
}
#jumbotron article img {
order: -1;
flex: none;
}
Listing 5-13.Flexbox Code to Style the Jumbotron Section
图 5-20 显示了结果,用 Chrome 渲染。注意<article>元素的flex的250px的底部宽度。这确保了所有的<article>以相同的宽度开始,并均匀地弯曲。这也意味着当使用 960 像素的窗口时,每行可以水平放置三篇文章。

图 5-20。
When the window is wider than 960px, three <article>s render per row
当窗口大小折叠或在移动设备上查看页面时会发生什么?Flexbox 的一个主要优点是它可以作为一个响应式设计工具。这段代码足够灵活,可以处理不同大小的窗口。当窗口变得更窄时,内容会重新分页,这样每行只出现两个<article>,如图 5-21 所示。

图 5-21。
When the window narrows, the content automatically reformats onto more lines
当以智能手机的分辨率观看内容时,也会发生同样的事情。您可以通过将浏览器窗口拖至尽可能小的宽度来模拟这种效果。在这种情况下,一旦窗口窄于 500px,内容将再次重新格式化,每行显示一个<article>(参见图 5-22 )。

图 5-22。
At smartphone resolutions the content renders a single <article> per line
福利区
福利区域稍微复杂一些,因为左侧部分的高度是各个徽章区域的两倍。这种布局包含嵌套的 flex 容器,但是列而不是行充当主轴。这使得可以将左侧物品上的弯曲设置为徽章弯曲的两倍。参见清单 5-14 和图 5-23 中的 Chrome 输出。

图 5-23。
The output in Chrome
/* The badge benefits section */
#benefits {
display: flex;
flex-flow: column wrap;
height: 260px;
}
#benefits article {
flex: 2 2 260px;
width: 318px;
}
#benefits article.badge {
display: flex;
flex-flow: row nowrap;
flex: 1 1 130px;
}
#benefits article.badge img {
order: -1;
flex: none;
}
Listing 5-14.CSS Code for the Benefits Area
注意文章被设置为使用flex: 2 2 260px,而article.badge使用flex: 1 1 130px。第二条规则覆盖了第一条规则,并强制所有徽章的大小保持一致。剩下的文章正好是徽章高度的两倍,其flex-grow和flex-shrink值为 2,而徽章值为 1。这使得<article>占据了徽章高度的两倍。
Note
需要一些额外的非 Flexbox CSS 代码来定义如图 5-18 到 5-23 所示的颜色、边框和印刷样式。
摘要
CSS 灵活的盒子布局提供了一个非常多才多艺的布局模型,非常适合创建响应用户界面元素。该模块使用基于轴的范例,它的内容可以根据通过flex-属性集定义的一些简单规则沿着轴伸缩。
Flexbox 解决了许多布局问题,到目前为止,这些问题都需要复杂的解决方案。在一个容器中集中排列多个项目现在变得很简单,就像对齐和调整盒子大小以使它们相互匹配一样。
浏览器支持很好,所以在网上使用 Flexbox 相当安全。大多数情况下,不支持的浏览器会自动退回到块级布局。您可以使用诸如 Modernizr 之类的库,这使得使用旧的 CSS 2.1 规范为这些浏览器提供多填充样式集变得很容易。
` | `` | | `
` | | | `
- ` | `` |
| `
- ` | `` |
| `` | `` |
| `` | `
` |除了
display: block、display: inline和display: none之外,还有另一个选项,它结合了display: block和display: inline. Display: inline-block的属性,在两者之间提供了一个折衷方案,允许元素内嵌放置,但占用指定的宽度和高度。Inline-block相对来说是 CSS 党的后来者,主要是因为在 CSS2.1 的早期,浏览器没有可靠地支持它。但它是另一个有价值的工具,也是帮助解决布局挑战的一个选择。清单 3-6 展示了display: inline-block的一个简单例子。结果如图 3-8 所示。图 3-8。
The output of Listing 3-6, where elements have been given structure thanks to
display: inline-block<style> .relative { position: relative; padding: 10px; width: 500px; background: red; margin: 10px; } .navitem { display: inline-block; width: 70px; height: 40px; line-height: 40px; text-align: center; background: grey } </style> <div class="relative"> <h1>Example of inline-block</h1> <p>Here is some content <span>that includes an inline <span> element</span>.</p> <p>Here's some more content, and this time the <span> elements have the class navitem applied to them: <span class="navitem">Item 1</span> <span class="navitem">Item 2</span> <span class="navitem">Item 3</span></p> <p>Note that inline-block elements contribute to the layout.</p> </div> Listing 3-6.Using inline-block to Give Inline Elements Structure in the Form of Width and Height显示属性如何影响布局?
如您所见,您对页面上元素的绘制方式有很大的控制权。
display属性直接影响布局。默认情况下,一些元素的width和height不能被显式设置,因此它们不能影响周围的元素或它们在页面上的位置。其他的默认有影响。自然,您可以使用 CSS 中的display属性覆盖任何元素的默认属性。让我们快速地看一下它的运行情况。清单 3-7 结合了相对和绝对定位;浮动元素;和内联块元素合并到一个页面中。这并不罕见;你通常会在最复杂的布局中找到各种布局的例子。列出 3-7 的结果如图 3-9 所示。
图 3-9。
The output of Listing 3-7. Note that different layout paradigms are suited to different solutions, so it’s common to use a range of tools to achieve a specific part of the overall layout
<style> .relative { padding: 10px; position: relative; width: 920px; background: #efefef; border: 1px solid #ccc; margin: 10px auto; } #header { position: relative; padding: 10px; height: 80px; line-height: 80px; background: #999; color: #fff } #header h1 { font-weight: normal;margin:0;padding:0;} #search { position: absolute; top: 10px; right:10px; } #search input { padding: 5px; } #nav { position: relative; height: 30px; background: #ccc; } #nav ul { margin: 0; padding: 0; } #nav ul li { float: left; list-style:none; padding: 0; margin: 0 3px; width: 70px; height: 30px; line-height: 30px; text-align: center; background: #ddd; } .sidebar { float: right; width: 200px; background: #ebebeb; padding: 10px; } .ib { display: inline-block; width: 70px; line-height: 30px; text-align: center; background: #ddd; border: #ccc; margin: 5px; } .clearfloat { clear: both; } </style> <div class="relative"> <div id="header"> <h1>Site Name</h1> <form id="search"><input type="text" value="search site" /></form> </div> <div id="nav"> <ul> <li><a href="#">Home</a></li> <li><a href="#">About</a></li> <li><a href="#">Contact</a></li> </ul> </div> <div class="sidebar"> <h3>Popular CSS modules</h3> <p><span class="ib">CSS Flexbox</span><span class="ib">CSS Multi-Column</span><span class="ib">CSS Regions</span><span class="ib">CSS Grid Layout</span><span class="ib">CSS Shapes</span></p> </div> <div id="content"> <h2>Welcome to our website</h2> <p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Curabitur blandit tempus porttitor. Maecenas faucibus mollis interdum. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum.</p> <p>Donec id elit non mi porta gravida at eget metus. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Curabitur blandit tempus porttitor. Donec sed odio dui. Maecenas sed diam eget risus varius blandit sit amet non magna.</p> <p>Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Donec id elit non mi porta gravida at eget metus. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Nulla vitae elit libero, a pharetra augue.</p> <p>Sed posuere consectetur est at lobortis. Sed posuere consectetur est at lobortis. Maecenas sed diam eget risus varius blandit sit amet non magna. Nullam id dolor id nibh ultricies vehicula ut id elit.</p> </div> <br class="clearfloat" /> </div> Listing 3-7.Combining relative, absolute, float, inline, block, and inline-block Elements into One Example Layout表格布局
本章讨论的最后一个布局工具是表格布局。正如你在第 1 和 2 章中所看到的,从网络的早期开始,表格就被用来实现布局。一旦 CSS 变得可用和可靠,Web 上就出现了强烈的反表格布局运动,结果表格布局几乎成了禁忌。这是一种基于 HTML 中的表格仅仅是为了使用表格数据而形成的观点,而不是为了页面的布局和显示。
因此,区分 HTML 表格和 CSS 表格是很重要的。这里讨论的表格布局是通过将
display: table分配给一个容器元素实现的。将display: table应用到现有的页面元素和使用 HTML<table>元素的区别在于,根据定义,<table>元素是并且将永远是一个表格。将display: table设置为属性的<div>可以简单地通过编辑 CSS 代码或使用@media查询来更改为使用不同的布局范式。CSS 表格有利于布局!CSS 表格布局提供了一种基于样式的解决方案,以类似于 HTML 表格但又不完全相同的方式呈现内容。每个不同表格元素的相应属性值如下:
| `table { display: table }` | | `tr { display: table-row }` | | `thead { display: table-header-group }` | | `tbody { display: table-row-group }` | | `tfoot { display: table-footer-group }` | | `col { display: table-column }` | | `colgroup { display: table-column-group }` | | `td, th { display: table-cell }` | | `caption { display: table-caption }` |清单 3-8 展示了如何使用其中的每一个来实现基于表格的布局,以及该方法提供的所有好处。结果如图 3-10 所示。
图 3-10。
A page layout achieved using
display: table. This is practically impossible to achieve without table layout, unless you use scripts or CSS3! Note that some properties have been ignored and aren’t rendered in the final page.display: tableworks just like an HTML table!<style> .table { display: table; border: 1px solid red; background: #eee; margin: 2px; padding: 2px; } .tr { display: table-row; border: 1px solid red; background: #ddd; margin: 2px; padding: 2px; } .td { display: table-cell; border: 1px solid #red; width: 200px; background: #ccc; margin: 2px; padding: 2px; } </style> <div class="table"> <div class="tr"> <div class="td"><p>This is a normal div, but displays like a cell in a table!</p></div> <div class="td"><p>This is another table cell</p></div> </div> <div class="tr"> <div class="td"><p>Here is a third cell, in a second row</p></div> <div class="td"><p>And a final cell.</p></div> </div> </div> Listing 3-8.Creating a Layout Using display: table Properties to Achieve Something that’s Very Difficult Using Alternative CSS2.1 Layout OptionsCSS 表格布局是一个非常有用的布局工具,是无数场景中的最佳选择。在 CSS3 出现之前,实现三列布局是一个很好的选择,其中各个列匹配最长列的高度,而不需要脚本或黑客。这也是一种垂直对齐内容的有用方式,而不必求助于负边距或其他类似的解决方法。但是这本书是关于 CSS3 的——让我们不要花太多时间担心
display: table。您可能会遇到这种情况,作为一种潜在的布局工具,它当然不容忽视,但您阅读本书并不是为了了解基于表格的布局!本章的要点是 CSS 布局模块不是孤立存在的。你几乎不可能遇到一个设计不包含 CSS 布局选项的样式。成功的设计师使用最好的工具,所以在设计页面时,不要害怕调用 CSS2.1 布局模块。在设计元素中找到最适合您的特定需求的布局模块,并使用它。
摘要
在本章中,您已经看到了 CSS3 之前可用的布局选项的高级概述。一些基本的例子有助于解释这些选项,本章还谈到了使用这些模块的一些布局限制。设计网站时,您每天都会用到所有这些布局模块;因此,如果你的胃口被激起,你想进一步阅读,去看看涵盖 CSS2.1 的书籍。
现在你已经准备好使用 CSS2.1 创建布局了。让我们直奔主题,开始学习新的东西:是时候探索 CSS3 布局了!
四、CSS 多列布局
CSS 多列布局模块提供了一个解决方案,这个方案解决了 web 设计者从 Web 诞生之初就面临的一个基本布局问题:如何排列内容,使其占据多个垂直容器,如报纸或杂志。多年来,聪明的网页设计者开发了各种变通方法来创建多栏布局。最初,这涉及到使用表格来对齐内容列,就好像每个元素都是单元格中的一个值一样。最近,浮动元素、清除元素和偶尔的 JavaScript 的巧妙组合提供了更具语义的解决方案,但这些方法都不太适合创建多栏布局。新的 CSS3 模块克服了许多与生成这种布局相关的问题,自动处理内容的流动和分发;而且浏览器支持已经相当不错了,所以今天开始使用这个模块还是相对安全的!
Note
虽然浏览器的支持已经很好了,但是浏览器厂商还在实现这个模块。所有常见的警告都适用!
什么是 CSS 多栏布局模块?
CSS 多列布局提供了一种将内容区域划分成列的解决方案,内容可以自动分页。与本书中涉及的其他全新布局模块不同,多列布局模块扩展了现有的 CSS box 模型。因为新模块建立在现有布局范例之上,所以回退是自动处理的。不理解多栏布局属性的浏览器会忽略它们,将内容区域呈现在单个栏中,而不是呈现为多个栏;见图 4-1 。
图 4-1。
CSS Multi-column Layout in action versus a fallback non-columnar layout in the same container
语法和结构
CSS 多列布局模块扩展了现有的 CSS 框模型,总共增加了十个属性。其中的每一个都提供了对内容呈现到容器中的列的方式的一个方面的控制。
因为所有属性都影响单个容器,所以从浏览器的角度来看,该模块非常容易理解,而且更重要的是,实现起来非常简单。因此,“在野外”对这个模块的支持是极好的!如果您的用户使用他们选择的最新版本的浏览器,您的内容将以列的形式呈现,每个常用的浏览器都支持此模块。
Note
与任何新的 CSS3 布局模块一样,在所有浏览器和操作系统组合中进行彻底测试总是值得的。您可以在
http://caniuse.com/查看最新的浏览器对 CSS 多栏的支持表。基本概念
CSS3 多列布局模块引入了十个可以应用于块级元素的新属性。除了控制列数、分布、宽度和分段的新属性之外,还有一些新的关键字可用于帮助控制内容跨列拆分的方式。
使用多栏布局模块创建多栏布局非常简单。没有不必要的复杂属性需要学习;因为所有事情都发生在单个元素中,所以除了你熟悉的 CSS2.1 的规则之外,没有必要担心内容如何在容器外包装。
仅使用这十个属性,您就可以执行以下操作:
- 将现有的单列元素转换为多列元素
- 内容自动跨列换行
- 控制列填充可用空间的方式,或者通过扩展以填充容器,或者始终遵循预设大小(这对于创建响应性布局很方便)
- 定义出现在列之间的边框和装订线宽度
- 呈现内容,使其跨越多列,以便在需要时打破分栏布局
关于溢出内容的呈现方式以及内容跨列分隔的方式,有一些特殊的规则。你将在本章的后面详细了解这些。
值得一提的是 CSS 多列布局允许您做的事情列表中的第三项。开箱即用,CSS 列可以快速响应。这意味着,如果您希望针对多种不同的设备配置对内容进行定位和分页,这是一个理想的布局模块。有很多方法可以打破这种固有的响应能力,但接下来的几页将重点介绍需要注意的事项。
CSS 列的响应特性不仅限于文本。图像也遵循分栏布局,因此通过使用典型的响应图像技术,您还可以使图片缩放以适合用于查看页面的屏幕。这是一个很好的节省时间的方法,也是值得你花时间熟悉这个模块的另一个原因。
我希望你相信 CSS 多列布局是你布局武库中的一个有用工具。让我们深入研究一下代码,看看它是如何工作的。
理解术语
与本书中涉及的一些其他布局模块不同,CSS 多列布局模块使用了我们都熟悉的现有布局范例。这使得理解和开始在你的布局中使用模块变得非常容易。也就是说(以防你对列的概念不太熟悉),让我们快速了解一下列这个词在 CSS 环境中的含义。
列是在整个容器中呈现内容的垂直划分。一个容器可以包含一列或多列,内容根据所用语言的规则从一列流向下一列。如果您用英语创作,列从左到右呈现。当内容溢出第一列时,它开始在下一列的顶部呈现。当所有的列都被使用时,根据正常的 HTML/CSS 规则,内容会溢出,尽管这可能会导致额外的列——您稍后会看到这一点。
CSS 多列模块的好处之一是,默认情况下,容器的高度会自动计算以容纳所有内容。这很有用,也是使用该模块优于一些旧的变通解决方案的主要好处之一。开箱即用,内容还会自动在所有列之间平衡,从而产生一个令人满意的内容块,它被划分为整齐的列,以符合您的规范。
图 4-2 展示了 CSS 多栏布局讨论中使用的不同属性。你可能会很快学会,因为它很直观;但是如果你不确定
column-fill和column-span之间的区别,这个数字还是值得参考的。图 4-2。
The terms used to talk about multi-column layouts in CSS3
在全神贯注于理论和财产名称/价值之前,让我们看一个例子。基本多栏布局的代码如清单 4-1 所示,您可以在图 4-3 中看到一个输出示例。在接下来的几页中,我将解释这个例子中发生了什么。注意,与所有使用供应商前缀属性实现的新兴模块一样,您可能需要添加
-webkit-或-moz-前缀来使其显示在您的浏览器中。图 4-3。
The resulting appearance from the combination of HTML and CSS code in Listing 4-1, shown at normal desktop resolution of greater than 960px width
<style> .multicol { position: relative; margin: auto; max-width: 960px; columns: 4 12em; column-gap: 2em; column-rule: 1px solid red; } .multicol p { padding: 0.25em; } .multicol figure { margin: 0; padding: 0; width: 100%; } .multicol figcaption { color: #999; font-size: 0.7em; } .multicol img { width: 100%; } .multicol h1 { column-span: all; margin: 0.25em 0; padding: 0; } .multicol h2 { margin: 0.25em 0; padding: 0; } </style> <article class="multicol"> <h1>Moby Dick; Or the Whale</h1> <h2>by Herman Melville</h2> <figure> <img src="img/mobydick.jpg" alt="Cover of Moby Dick" /> <figcaption>Voyage of the Pequod, illustrated by Everett Henry</figcaption> </figure> <p>Call me Ishmael. Some years ago—never mind how long precisely—having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little ... How then is this? Are the green fields gone? What do they here?</p> </article> Listing 4-1.HTML Markup Being Styled with CSS Multi-column Layout Properties这个例子真正有用的地方在于它能立即响应,正如你在图 4-4 和 4-5 中看到的,我已经缩小了浏览器窗口的宽度来模拟更小的屏幕。让我们看看这个例子的不同元素。
图 4-5。
The result of Listing 4-1 rendered at 320px wide—the most common smartphone resolution
图 4-4。
The same rendering shown with a reduced browser window width Note
需要一些额外的非多栏布局代码来定义图 4-3 、 4-4 和 4-5 中所示的颜色、边框和印刷样式。我还删减了清单 4-1 中标记的段落内容以节省空间。
HTML 标记
我不需要谈论 HTML,因为您正在使用标准的语义元素作为内容的容器。使用 CSS 多列布局没有特殊的标记要求;使用此模块,任何块级元素都可以应用多个列。
多列模型
关于图 4-3 和清单 4-1 中所示的例子,有一些事情需要注意,特别是关于内容如何在容器中呈现。在最初的 CSS 盒模型中,元素的内容流入该元素的内容盒。CSS 多列布局引入了一种新类型的容器,它存在于内容框和实际内容之间。W3C 称之为列框(简称为 column)。内容在一个元素内自动跨列流动,以内嵌(或阅读)方向流动。在基于拉丁语的语言(如英语)中,这是从左到右的。
列被排列成行。一行中的所有列都有相同的宽度和高度。列之间可以有间隔,这被称为列间距(或印刷术语中的装订线)。在大多数屏幕情况下,拆分成列的元素只有一行。但是,在特殊情况下,一个元素可能包含多行列。打印的文档也可以由多行组成,其中一个内容区域占据多个打印页面。在本章的后面,您将看到一个屏幕上多行的示例。
我在解释 CSS3 多栏布局时使用 W3C 的语言。出于这个原因,当谈到应用了多列属性的容器时,我引用了
multicol元素。多列(multicol)元素是其column-width或column-count未被设置为auto的任何容器。如您所见,在这种情况下,我使用了一个<article>元素。为了避免混淆,我也使用multicol作为属于这个元素的类的名称。下一节中涉及的所有单个属性都应用于
multicol元素。在 CSS3 中,不可能在单个列上设置属性和值。明确地说,这意味着您不能为某一列指定background、padding或margin。Note
应用于
multicol元素的Padding和margin被应用于容器,而不是它的列虽然您不能控制单个列,但理解浏览器呈现器将它们视为独立的块级框(就像表格单元格一样)是很重要的。每个列框充当其内容的包含块。这意味着,例如,应用于在列中呈现的段落元素的
margin和padding被应用于该列的包含块的边缘。CSS 属性
查看清单 4-1 和图 4-3 、 4-4 和 4-5 中所示示例的 CSS 代码,看起来不熟悉的第一段代码应该是使用了
columns属性。这是为两个 CSS 属性定义值的简写(参见清单 4-2 )。.multicol { columns: 4 12em; } Listing 4-2.Defining the <article> with a class of multicol as Having Four Columns, Each of Width 12em正如我刚才提到的,
columns属性是两个 CSS 属性的简写:column-count和column-width。在这种情况下,我已经为.multicol元素分配了四列,每列的宽度为12em。需要注意的是,浏览器不一定会完全呈现您指定的内容。如果没有足够的空间来创建四列,每列的宽度为12em,渲染引擎会删除列以使其适合。结果宽度可能不是12em,列数可能不是四列!这意味着当您使用 CSS 多列布局模块定义列时,您不能确定浏览器将准确呈现您直观期望的内容。这听起来像是一个糟糕的实现;但是正如您将看到的,它是该模块最强大的方面之一的基础:CSS3 多列布局在默认情况下是响应性的。
列计数
column-count定义一个multicol元素应该被分成的列数。正如您将看到的,规则决定了浏览器是否尊重该属性。如果未指定或设置为auto,则根据column-width属性值划分空间。列宽
column-width指定了每列在multicol元素中应该占据的最小宽度。注意,我说的是最低限度,不是最终的!同样,渲染引擎遵循一系列逻辑规则来确定是否严格遵守该规则。如果未指定或设置为auto,列宽由拆分成的列数决定——根据column-count属性值。列
columns属性是组合column-count和column-width属性的有效快捷方式。根据您传递给columns属性的值,浏览器会以不同的方式解释您的意图,因此理解所有可能的排列很重要。我在代码清单 4-3 中借用了 W3C 的示例代码来帮助说明。记住这是如何工作的一个简单方法是,如果你使用单个值和一个单位,浏览器会将其解释为一个
column-width值。如果省略一个单位,该值将被解释为一个column-count值。如果您不确定,请指定两个值:一个有宽度单位,一个没有计数单位。columns: 12em; /* equates to column-width: 12em; column-count: auto */ columns: auto 12em; /* equates to column-width: 12em; column-count: auto */ columns: 2; /* equates to column-width: auto; column-count: 2 */ columns: 2 auto; /* equates to column-width: auto; column-count: 2 */ columns: auto; /* equates to column-width: auto; column-count: auto */ columns: auto auto; /* equates to column-width: auto; column-count: auto */ Listing 4-3.Effective Longhand Version of Each of Six Property Values Assigned Using the columns Shorthand Property删除列和更改宽度的规则
如前所述,浏览器的渲染引擎并不总是严格遵循您对
multicol元素的宽度和总列数的定义。W3C 的 CSS3 多列布局模块规范定义了如何以及何时改变呈现意图的规则。W3C 提供了一个伪代码列表来显示要应用的逻辑。我在清单 4-4 中复制了这个;上市后可以找我翻译的规则,不用担心看不懂这段代码!if ((column-width = auto) and (column-count = auto)) then exit; /* not a multicol element */ if ((available-width = unknown) and (column-count = auto)) then exit; /* no columns */ if (available-width = unknown) and (column-count != auto) and (column-width != auto) then N := column-count; W := column-width; exit; if (available-width = unknown) then available-width := shrink-to-fit; if (column-width = auto) and (column-count != auto) then N := column-count; W := max(0, (available-width - ((N - 1) * column-gap)) / N); exit; if (column-width != auto) and (column-count = auto) then N := max(1, floor((available-width + column-gap) / (column-width + column-gap))); W := ((available-width + column-gap) / N) - column-gap; exit; if (column-width != auto) and (column-count != auto) then N := min(column-count, floor((available-width + column-gap) / (column-width + column-gap))) W := ((available-width + column-gap) / N) - column-gap; Exit Listing 4-4.W3C’s Pseudocode for Calculating the Width and Number of Columns According to the Values Set for the column-width and column-count Properties下面是我对清单 4-4 中伪代码的翻译。请注意,规则和条件是按照以下顺序进行比较和操作的:
If the
column-countandcolumn-widthproperties are both either unset or set toauto, no columns are rendered. The same is true ifcolumn-countis set toautoand the width of themulticolelement is unrestricted. If thecolumn-countandcolumn-widthproperties both have values and a width hasn’t been specified for themulticolelement, use the values exactly as they are specified. If thecolumn-countproperty has been set with a value other thanautoand thecolumn-widthproperty has been set toauto, divide the available width into the number of columns specified. If thecolumn-widthproperty has been set with a value other thanauto, thecolumn-countproperty has been set toauto, and the container has a specified width, set the number of columns to match the available space divided by the specified width of each column. This may mean the columns end up wider than specified, if the width of themulticolelement doesn’t divide perfectly by the specified width of each column. If thecolumn-countandcolumn-widthproperties have both been set with a value other thanautoand the width of themulticolelement has been set, look at whether the combination of width and count will fit inside the container element. If not, reduce the number of columns to match the number of times the specified width of each column will fit in the available space, and then expand the column width to fill themulticolelement using the calculatedcolumn-count.我希望您可以看到这种方法不仅有意义,而且使 CSS3 多列布局模块非常灵活。它被设计为自动响应,同时尽可能接近地尊重指定的
column-count和column-width属性的值。列间隙
如果你关注清单 4-4 中的代码,你会发现一个我还没有讨论的新属性:
column-gap. column-gap用于定义列之间的装订线宽度。图 4-3 中的例子使用了column-gap。您可以在清单 4-5 中再次看到.multicol元素的完整定义。.multicol { position: relative; margin: auto; max-width: 960px; columns: 4 12em; column-gap: 2em; column-rule: 1px solid red; } Listing 4-5.Stylesheet Definition for the .multicol Element, Showing the column-gap Property in Use与
column-width和column-count属性不同,column-gap属性是绝对的。这意味着如果您指定了一个值,浏览器将尊重该设置,即使这意味着牺牲列。列规则
属性控制是否在呈现的列之间画一条分隔线。
Column-rule的工作方式与 CSS 1 和 2.1 中的border属性非常相似。事实上,与border属性一样,column-rule是三种不同属性的简写属性:Column-rule-width:和border-width一样,这个属性接受一个单位的参数,比如2px,或者关键字none。Column-rule-style:设置要渲染的线条样式。与border-style相同的选项可用,包括dotted、dashed、solid等。Column-rule-color:该颜色属性可以使用浏览器支持的任何颜色模型进行设置,包括hex和rgb()。
Note
列标尺始终绘制在列间距(装订线)的正中央。
列规则(见图 4-6 )呈现为好像该列完全被内容填充,不管这是否是真的。如果样式表定义要求平衡填充列以外的内容,这很有用,因为这有助于直观地定义列所占的空间,即使它没有内容。回想一下,使用浮动元素生成列来实现这个简单的效果是多么困难!
图 4-6。
The column rule in action
分栏符
通常,浏览器的渲染引擎决定如何以及在哪里在一个
multicol元素中跨列分解内容。这在许多情况下都很有效,但是有时您可能希望控制内容的分割方式。例如,您可能想要强制内容在每个<h3>标签之前的新列中开始呈现。有三个属性可用于控制如何以及何时中断内容:break-before、break-after和break-inside。先断后合
此属性指定应该在应用元素之前应用分隔符。它接受以下值:
auto|always|avoid|left|right|page|column|avoid-page|avoid-column。在 CSS3 多列布局的情况下,特别有趣的是column和avoid-column选项,它们确保或防止(在可能的情况下)在multicol元素中出现中断。中断后
此属性指定应该在应用了分隔符的元素之后应用分隔符。它接受以下值:
auto|always|avoid|left|right|page|column|avoid-page|avoid-column。在 CSS3 多列布局的情况下,特别有趣的是column和avoid-column选项,它们确保或防止(在可能的情况下)在multicol元素中出现中断。闯入内部
与其他两个选项不同,
break-inside控制内容在它所应用到的元素内部如何断开。它接受以下值:auto|always|avoid|avoid-page|avoid-column。在 CSS3 多栏布局的情况下,特别有趣的是column和avoid-column选项,它们确保或防止(在可能的情况下)一个multicol元素的中断。Note
当分栏符拆分一个框时,该框的边距、边框和填充对拆分位置没有视觉效果。但是,分割后的利润立即生效。
列跨度
column-span属性允许列中的块级元素超出该列的边界。将来可能会指定要跨越的列数,就像可以应用于表格单元格的colspan属性一样,但是在 CSS3 规范中只有两个可能的值:none和all。默认情况下,
column-span设置为none。这意味着内容在列的约束内呈现。如果它被设置为all,列将在元素被渲染的地方断开。这就产生了一种效果,一个multicol元素可以包含多行列。图像、响应和列中的裁剪
图像在布局中提出了一个特殊的问题,你不能绝对确定多少空间将被分配给它们的显示。有两种方法可以处理分栏布局中的图像:
- 用图像填充列宽,放大或缩小图像以匹配可用空间。
- 预先确定图像应该显示的大小,并根据需要环绕或裁剪图像以填充空间。
清单 4-1 中的例子,如图 4-3 、 4-4 和 4-5 所示,使用第一个选项。使用宽度为 100%的 CSS 规则会强制图像以整列宽度呈现。这有效地提高了图像的响应性,因为它可以根据可用空间的大小进行缩放。
另一种方法是以设定的大小呈现图像。就像任何其他块级容器一样,浮动元素在
multicol元素中工作,但是被限制在呈现它们的列中的空间内。此外,如果呈现的图像占据的水平空间比列中可用的空间多,则图像会在该列的有效内容框的边缘被裁剪。这两种不同的图像渲染方法的区别如图 4-7 所示。图 4-7。
Image 1 is set to 100% width, which allows it to scale to fit the column width. Image 2 uses an absolute size, forcing it to crop to the column width when the column is narrower than the image Note
如果您希望您的图像具有响应性,请使用第一种方法,因为它使图像能够根据可用空间调整大小。
控制如何用内容填充列
使用 CSS3 多列布局,可以通过两种方式在列中填充内容:可以让内容在列之间平衡,也可以按顺序填充,直到内容用完。如果使用后一个选项,有可能会呈现一个或多个没有任何内容的列。
属性提供了对浏览器使用哪种方法的控制。它接受两个可能的值:
balance和auto。正如您所料,balance在所有可用的列中平等地呈现内容,auto使用顺序方法。缺省(未指定)值是balance,所以如果您没有显式地将column-fill设置为auto,浏览器会尝试呈现平衡的列填充。清单 4-6 中的代码和图 4-8 中的浏览器内结果显示了使用两个选项呈现的相同内容。图 4-8。
The two different column-fill options. The first balances the content equally across the columns, and the latter fills columns sequentially
<style> .multicol1 { columns: 3 20em; column-fill: balance; } .multicol2 { columns: 3 20em; column-fill: auto; } </style> <div class="multicol1"> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis risus eget urna mollis ornare vel eu leo. Nulla vitae elit libero, a pharetra augue. Sed posuere consectetur est at lobortis.</p> <p>....</p> </div> <div class="multicol2"> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis risus eget urna mollis ornare vel eu leo. Nulla vitae elit libero, a pharetra augue. Sed posuere consectetur est at lobortis.</p> <p>....</p> </div> Listing 4-6.Two Different Column-Fill Approaches控制列内容溢出的方式
正如您看到的超出列边界的图像,延伸到列间隙的内容在列间隙的中间被剪切。再看一下图 4-7 中的图表,看看它是如何工作的。
当内容超出了为一个
multicol元素分配的空间时,浏览器可以根据你在 CSS 代码中使用的设置以几种不同的方式呈现。有时这可能会导致在multicol块之外呈现额外的列。理解这一点的最好方法是使用两个简单的例子。清单 4-7 中的代码创建了一个简单的有三列的
multicol元素,并在每个段落后填充包含强制分栏符的内容。当您在 HTML 代码中添加第四个段落时,浏览器会继续执行强制换行,并创建第四列来容纳附加的段落。这被呈现在multicol元素的内容框之外,因为有足够的空间来容纳指定的三列,并且你显式地声明了一个列宽(见图 4-9 )。图 4-9。
If you force a break after the end of each paragraph, adding a fourth paragraph forces an additional column to render outside the content box
<style> .multicol { width: 60em; columns: 3 18em; } p { break-after: column; } </style> <div class="multicol"> <p>Cras justo odio, dapibus ac facilisis in, egestas eget quam. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam quis risus eget urna mollis ornare vel eu leo. Nulla vitae elit libero, a pharetra augue. Sed posuere consectetur est at lobortis.</p> <p>Donec id elit non mi porta gravida at eget metus. Etiam porta sem malesuada magna mollis euismod. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum. Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p> <p>Nulla vitae elit libero, a pharetra augue. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Curabitur blandit tempus porttitor. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Vivamus sagittis lacus vel augue laoreet rutrum faucibus dolor auctor.</p> <p>Maecenas sed diam eget risus varius blandit sit amet non magna. Vestibulum id ligula porta felis euismod semper. Nullam quis risus eget urna mollis ornare vel eu leo. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec ullamcorper nulla non metus auctor fringilla. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p> </div> Listing 4-7.Example of an Extra Column Rendering Outside the Content Box在图 4-9 中,内容被强制插入到内嵌方向的附加列中。如果为
multicol元素显式指定了高度,并且要呈现的内容量超过了可用空间,也会出现相同的效果。与任何块级元素一样,如果您为
overflow属性指定了一个值,它就会被执行,从而能够控制是否在文档中呈现额外的列。避免生成额外列的最简单方法是不指定multicol元素的高度,并避免强制分栏。对于分页媒体(如打印页面)如何处理溢出的内容,也有特殊的规则。阅读 W3C 规范以获得完整的细节。
如何使用 CSS 多栏布局
正如您所看到的,CSS 多列布局提供了一个明智而实用的解决方案,可以像报纸一样将内容排列成列。也许该模块最大的好处是它给网页设计过程带来的工作流程的改进,使填补和变通成为过去。此外,该模块在默认情况下具有响应能力,为移动优先的设计方法提供了一个真正有用的选项,同时减少了在所有屏幕分辨率下实现有效解决方案所需的开发时间。
CSS 多列布局是一个很棒的整体布局工具,但它也可以用来创建更小的页面内用户界面元素,例如:
- 内容在大屏幕上排列成栏,但在移动设备上退回到单列的表单
- 水平导航菜单,其中每个元素占据相同的水平空间是很重要的(尽管在 Flexbox 上查看第五章可以获得更好的选择!)
- 在您事先不知道有多少内容或水平空间可用的区域呈现内容
也就是说,CSS 多栏布局的主要目的是作为一个整体的页面布局工具,这是你可以从中获得最大好处的地方。
浏览器支持
浏览器对 CSS 多栏布局的支持已经很不错了。每个主流浏览器都支持至少两个版本。Internet Explorer 目前拥有最完整的实现,从版本 10 开始,当前版本提供对该规范的完全支持和部分支持。Firefox、Chrome、Safari 和 Opera 也都提供了对该规范的部分支持,而基于 WebKit 的浏览器提供了完全支持。在移动平台上,情况是相似的:所有常见的浏览器都有很好的支持,IE mobile 提供了最完整的实现。
CSS 多列布局模块在 2011 年 4 月达到候选推荐状态,这实际上意味着它是一个完整的模块,你可以认为它是稳定的。尽管如此,CSSWG 仍在继续努力完善和进一步开发该模块。您可以在
http://caniuse.com/#feat=multicolumn.查看最新的浏览器对 CSS 多栏布局的支持Caution
尽管浏览器对该模块的支持非常好,但该标准的旧实现可能包含错误或缺陷。与任何新模块一样,为了确保最佳结果,您必须在所有可能的浏览器配置中进行测试。不要假设每个浏览器都会相同地呈现你的
multicol元素。后备选项和聚合填充
不理解 CSS 多栏布局属性的浏览器会对每个元素使用默认值。这意味着在大多数情况下,设计为具有多列的元素会在单个列中呈现。对于许多布局来说,这并不是什么大问题,而且对于布局的渐进增强方法来说,这是一个合理的结果。如果你的布局中必须有列,不管浏览器是否支持 CSS 多列布局,使用 Modernizr 框架将允许你识别和处理不能显示 CSS 列的浏览器。您可以在
http://modernizr.com.了解更多信息真实世界的例子
尽管多栏布局并不是什么新东西,但新模块带来的工作流程改进对网页设计者来说意义重大。因此,很值得探索一个真实世界的例子,看看一切是如何组合在一起的。你不会被一个全新的视觉范例所震撼,但是如果你在网络上工作过一段时间,你会欣赏代码的美丽!
模型
对于这个例子,我为一份虚构的在线报纸创建了一个页面设计模型。渲染输出如图 4-10 所示。本示例使用 CSS 多列布局模块呈现设计中显示的所有列,包括布局顶部的嵌套列,其中一行有三个标注。它还使用了本章中讨论的一些特性,包括
column-span属性来创建有效的引用。图 4-10。
A mockup for a fictional online newspaper
HTML 标记
这个页面的 HTML 标记非常简单。其核心是一组元素,每个元素包含一篇文章。无论用户的浏览器是否支持 CSS 多列布局,标记都可以工作。清单 4-8 显示了整个页面的 HTML 代码。
<div id="container"> <header> <hgroup> <h1><span>the</span>news</h1> <h2>Your daily news</h2> </hgroup> <nav id="primarynav"> <ul> <li>News</li> <li>Sport</li> <li>Culture</li> <li>The Arts</li> <li>Business</li> <li>Economy</li> <li>Lifestyle</li> <li>Travel</li> <li>Technology</li> <li>Comment</li> </ul> </nav> </header> <section id="news"> <nav id="sectionnav"> <ul> <li>News</li> <li>US & Canada</li> <li>World</li> <li>Politics</li> <li>Media</li> <li>Education</li> <li>Science</li> <li>Entertainment</li> <li>Weather</li> </ul> </nav> <div id="pagelayout"> <div id="mainlayout"> <div id="callouts"> <div class="callout1"> <img src="img/callout1.jpg" /> <h2>Banks in Recovery</h2> </div> <div class="callout2"> <img src="img/callout2.jpg" /> <h2>Weekend ideas</h2> </div> <div class="callout3"> <img src="img/callout3.jpg" /> <h2>New museum set to open</h2> </div> </div> <!-- End Callouts --> <article> <h2>Island boat service due to stop in September</h2> <figure> <img src="img/article.jpg" /> <figcaption>Above: The service provides a lifeline to the local island community</figcaption> </figure> <p>Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus, ....</p> <h3>Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit. Nullam id dolor id nibh ultricies vehicula ut id elit.</h3> <p>Etiam porta sem malesuada magna mollis euismod. Duis mollis, est non commodo luctus....</p> </article> </div> <!-- End Main Layout --> <aside> <div id="promoted"> <article> <img src="img/promoted1.jpg" /> <h3>Jobs of the future</h3> <p>Susan Hill explains why the jobs market our children will face bears no relation to today's workplace</p> </article> <article> <img src="img/promoted2.jpg" /> <h3>10 amazing getaways</h3> <p>Our top pick of summer breaks on a budget, that offer a lot of bang for your bucks</p> </article> </div> <!-- End Promoted --> <div id="features"> <h4>Features</h4> <ul> <li> <img src="img/featured1.jpg" /> <h5>Rise of the cupcake</h5> <p>Inceptos Aenean Dolor Sit Commodo</p> </li> <li>...</li> </ul> </div> <!-- End Features --> </aside> </div> <!-- End Page Layout --> </section> </div> Listing 4-8.HTML Code for the CSS Multi-column Layout Page呈现列
仅使用 CSS2.1 肯定可以实现这种布局,但是新的 CSS 多列布局模块使得维护页面上的内容更加容易,而不必调整标记或手动对内容进行分页。我将单独向您展示每一部分,以便您可以看到 CSS 多列布局模块是多么有用。
Note
与许多 CSS3 属性一样,在实现阶段,浏览器供应商为属性添加了前缀。我将向您展示不带前缀的代码,以保持清单的整洁。检查您的目标浏览器,看看您是否需要使用每个属性的供应商前缀版本。
主要文章
主要标题和文章区域遵循贯穿全书的逻辑设计模式。CSS 如清单 4-9 所示。这是实现如图 4-10 所示的柱状布局所需的全部代码。
#mainlayout { max-width: 560px; columns: 2 250px; column-rule: 1px solid #999; column-gap: 20px; } #mainlayout article p { font-size: 1.1em; line-height: 1.7em; } #mainlayout article figure { width: 100%; margin: 0; padding: 0; } #mainlayout article figcaption { font-style: italic; font-size: 0.8em; } #mainlayout article figure img { width: 100%; border: 1px solid #297C21; } #mainlayout article h2 { color: #297C21; column-span: all; font-size: 2.2em; font-weight: normal; } #mainlayout article h3 { column-span: all; padding: 10px 0px; border-top: 1px solid #999; border-bottom: 1px solid #999; font-weight: normal; color: #333; font-size: 1.5em; line-height: 1.5em; } Listing 4-9.CSS Code Used to Create the Main Article’s Multi-column Layout这段代码中的简单规则创建了在主文章区域中呈现两列所需的布局,并打破了 pullquote 的列结构。结果是在 pullquote 的上方和下方呈现一组列行。注意,代码还将
column-span: all用于<h2>标记,这将跨两列呈现标题。你可以在图 4-11 中看到输出,这是在 Safari 中渲染的。图 4-11。
The primary article rendered in Safari
其他列内容
页面上的其他列和内容很容易呈现。使用相同的原则,但值稍有不同,可以呈现顶部的标注部分。因为这个标注区域是主容器的子容器,所以首先需要跨越标注区域的两列,然后为容器设置一个
3的column-count,允许三个标注呈现在三列中。HTML 再次显示在清单 4-10 中,相关 CSS 显示在清单 4-11 中,结果呈现在图 4-12 中。图 4-12。
The three-column callout area sitting above the two-column article, rendered in Safari
<div id="mainlayout"> <div id="callouts"> <div class="callout1"> <img src="img/callout1.jpg" /> <h2>Banks in Recovery</h2> </div> <div class="callout2"> <img src="img/callout2.jpg" /> <h2>Weekend ideas</h2> </div> <div class="callout3"> <img src="img/callout3.jpg" /> <h2>New museum set to open</h2> </div> </div> <!-- End Callouts --> ... </div> Listing 4-10.HTML for the Callout Area#callouts { column-span: all; columns: 3 180px; } Listing 4-11.CSS Applied to the HTML in Listing 4-10, Spanning the #mainlayout Columns and Setting Up a Three-Column Layout for the Callout Area剩下的部分是右边较小的列区域。这以类似于主内容区域的方式呈现,但是它包括堆叠列表项和特性。相关的 HTML 代码如清单 4-12 所示。
<aside> <div id="promoted"> <article> <img src="img/promoted1.jpg" /> <h3>Jobs of the future</h3> <p>Susan Hill explains why the jobs market our children will face bears no relation to today's workplace</p> </article> <article> <img src="img/promoted2.jpg" /> <h3>10 amazing getaways</h3> <p>Our top pick of summer breaks on a budget, that offer a lot of bang for your bucks</p> </article> </div> <!-- End Promoted --> <div id="features"> <h4>Features</h4> <ul> <li> <img src="img/featured1.jpg" /> <h5>Rise of the cupcake</h5> <p>Inceptos Aenean Dolor Sit Commodo</p> </li> <li>...</li> </ul> </div> <!-- End Features --> </aside> Listing 4-12. Sidebar Content Area对于页面的这一部分,CSS 实际上非常简单。唯一需要的 CSS3 多列布局代码强制第二列成为一个新列,并首先创建这两列。重要的位如清单 4-13 所示;结果如图 4-13 所示。
图 4-13。
The resulting layout shows two columns in the sidebar area. Note that I’ve used a combination of image techniques in this layout, which is rendered in Safari
#pagelayout > aside { max-width: 350px; columns: 2 160px; margin-left: 10px; } #pagelayout > aside img { width: 100%; } #features { break-before: column; } #features ul li img { clear: left; width: 35%; float: left; } Listing 4-13.Pertinent Bits of CSS Used to Achieve the Two-Column Layout in the Sidebar记住 CSS3 多栏布局是默认响应的。这意味着随着可用水平呈现空间的减少,主布局中呈现的列数也会减少。回想一下,在主文章区域中显示了一组嵌套的列。因为您没有为这些列指定最大或固定的宽度,所以它们会收缩,直到达到 220px 的最小宽度。一旦它们达到那个宽度,它们就不能再变小了;相反,浏览器在单个列中呈现内容,该列可以扩展以填充可用空间。你可以在图 4-14 中看到这个效果。
图 4-14。
Notice that when the browser window is pulled in to be narrower than a typical desktop resolution, the content automatically reformats to reduce the number of columns
当以智能手机分辨率查看内容时,也会发生同样的响应性重新格式化。您可以通过将浏览器窗口拖至尽可能小的宽度来模拟这种效果。在这种情况下,一旦窗口窄于 500px,内容将再次重新格式化,在一个连续的列中显示所有主要内容,如图 4-15 所示。
图 4-15。
On a smartphone, the page automatically responds, rendering content in a single column layout rather than in columns side by side Note
需要一些额外的非 CSS 多栏布局 CSS 代码来定义颜色、边框和印刷样式,如图 4-10 到 4-15 所示。
摘要
CSS 多列布局提供了一种非常有用的方法,可以将内容呈现为好看且易于维护的读者友好的列。这是新模块的主要优势,因为它简化了这些复杂布局的创建,使未来更新内容的速度大大加快,并且首先需要更少的黑客和变通方法来创建布局。
浏览器支持非常好,所以这是在网络上使用的所有新 CSS3 布局模块中最安全的一个。不支持的浏览器会自动退回到单列布局。通过为各个列使用指定的宽度,布局在默认情况下也会做出响应,自动重新格式化以适应更小或更大的屏幕,而无需任何进一步的代码。
使用像 Modernizr 这样的库,可以很容易地为不兼容的浏览器提供一组使用旧的 CSS 2.1 规范的多填充样式。但是在很多情况下,这并不是绝对必要的,因为大多数布局都没有分栏来分隔内容。
五、CSS 灵活的盒子布局
CSS 灵活的盒子布局模块解决了一个定位问题,这个问题是 web 设计者从 CSS 诞生的第一天起就一直在努力解决的:沿水平或垂直轴均匀地间隔元素,而不需要求助于复杂的浮动或基于脚本的技巧。这一章将更详细地介绍灵活的盒子布局模块,并展示它如何革新你设计网页的方式。
Note
该模块通常被称为 Flexbox,因此在本章中,我交替使用术语 Flexbox Layout 和 Flexbox 来指代该模块。
什么是 Flexbox?
CSS 灵活的盒子布局提供了一个为用户界面设计优化的盒子模型。使用 flex 布局模型,启用 flex 的容器的子元素可以被布置在轴上(水平或垂直),并且这些子元素可以自动增长和收缩以填充可用空间,而不会溢出父容器。Flexbox 模块的一个特别的好处是易于设置和操作子元素的对齐。这使得定位内容变得简单,同时保留了以后引入其他兄弟元素的灵活性:使用基于像素的布局的日子已经一去不复返了。
也可以将单个 flex 容器嵌套在其他 flex 容器中。通过将垂直容器嵌套在水平容器中,您可以构建灵活的跨两个轴的布局,反之亦然。
CSS 2.1 引入并定义了以下四种用于呈现网页的布局模式:
- 文档的块布局
- 文本的嵌入式布局
- 二维网格中表格数据的表格布局
- 定位布局,用于在页面上显式定位元素,并将其从文档流中移除
浏览器在解析 CSS 规则时使用这些系统。这些布局模式根据元素的同级、文档中的流动和父元素来确定元素的大小和位置。
Flexbox 引入了第五种布局模式,W3C 将其命名为 flex layout(见图 5-1 )。这种模式是专门为排列复杂的用户界面元素而设计的。Flex-layout 模式考虑了比 CSS2.1 更复杂的页面和 web 应用程序的使用场景。
图 5-1。
Flexbox in action, rendering a page without the need to use workarounds to achieve the layout. You build this layout at the end of this chapter
语法和结构
W3C CSSWG 自 2009 年第一份工作草案发布以来一直致力于 Flexbox 模块。在此期间,规范和语法发生了实质性的变化。目前的规范包括 2012 年 9 月发布的候选人推荐和 2013 年 10 月更新的编辑草案。
一个候选推荐被认为是稳定的,并且这一个形成了本章中详细描述的语法的基础。虽然没有太大的不同,但编辑的草稿引入了一些我也提到的改进。我会指出,这里显示的语法来自编辑的草稿,而不是候选人的推荐。此外,我会让您知道浏览器目前提供的支持。
Note
截至 2013 年 10 月 3 日,Flexbox 模块的最新版本是基于 W3C 候选推荐标准的编辑草案。编辑的草稿是公开讨论的,可以随着时间的推移而改变。因此,语法不能被认为是稳定的。为了避免问题,请确保您使用的是最新的候选人推荐。您可以在
http://dev.w3.org/csswg/css-flexbox/查看当前编辑的规范草案。基本概念和术语
在对 Flexbox 的介绍中,W3C 将 flex 布局描述为表面上与块布局相似,因为它们遵循相似的设计模式。我发现把 flex layout 想象成有点像 table layout 是有帮助的,因为它允许元素相对于轴对齐和调整大小,就像表格的单元格被挤压成一行一样。正如您将看到的,flex 布局实际上非常不同,尽管它确实具有块、内联和表格布局的可识别特性。
本质上,flex 布局很简单。但是不要被骗了,以为 Flexbox 功能没那么强大,因为它很容易上手。这是一个非常通用的布局模块,允许您执行以下任务:
- 以四种不同方向之一布局元素:从左到右、从右到左、从上到下或从下到上
- 仅使用 CSS 重新排列元素的顺序
- 自动调整元素大小以适应可用空间
- 根据容器或同级元素对齐元素,实现通用的跨轴比例
- 折叠容器内的元素,而不影响容器的偏移量
- 创建线性单轴布局或沿横轴缠绕的多线布局
灵活布局模式的核心是轴的概念。Flexbox 是一个二维布局工具,有两个可能的工作轴:水平,简称
row;还有垂直的,简称column。Flexbox 布局需要一个元素作为 flex 容器,以及零个或多个作为 flex 项目的子元素。这些 flex 项目使用 flex 布局模型进行布局,而父容器可以应用其他布局模型(如 float)。这意味着您可以将 flex 容器合并到您的标准 CSS 2.1 布局中,而不必改变整个结构方法。
浏览器支持
浏览器支持灵活的盒子布局已经很不错了。Internet Explorer 从版本 10 开始就部分支持该规范,IE11 中包含了完全支持。Firefox、Chrome、Safari 和 Opera 都至少部分支持该规范,基于 WebKit 的浏览器提供完全支持。
在移动平台上,iOS Safari 有很好的支持,黑莓的浏览器也是如此。IE mobile 和 Android 的浏览器都有部分支持。只有 Opera Mini 目前不支持该规范。您可以在
http://caniuse.com/#search=flex查看 Flexbox 最新的浏览器支持。Caution
在灵活盒子布局模块的开发过程中,一些浏览器实现了该规范的早期版本。语法已经改变了。因此,Web 上的许多示例现在都已经过时了,所以在遵循示例代码时要小心,以确保您使用的是模块的正确版本。
不理解 Flexbox CSS 属性的浏览器会对每个元素使用默认值。通常情况下,
<div>、<section>和<article>等结构项显示为块级元素,而<span>等内联元素则恢复为默认的内联级行为。您可以利用这些信息来处理较旧的浏览器;或者,如果您想填充类似 Flexbox 的渲染,Modernizr JavaScript 库允许您测试浏览器支持。您可以在http://modernizr.com了解更多信息。方向和大小
要完全理解 Flexbox 容器的流程方向的含义以及起点和终点在哪里,使用通用语言定义 Flexbox 模块的相关方面会有所帮助。W3C 在灵活盒子布局的候选推荐草案中已经在这方面做了很好的尝试,所以我在讨论方向和大小时使用了相同的方法。
flex 容器的主轴是沿其放置各个 flex 项目的轴。主开始和主结束是根据页面的语言定义的,并与主尺寸的开始和结束对齐。横轴垂直于主轴延伸,并具有十字起点和十字终点。同样,这些是由交叉大小定义的。
图 5-2 展示了我在讨论 Flexbox 时使用的不同轴和命名约定。你可能想回头参考这个图,直到你对两个轴的概念感到舒服为止。
图 5-2。
Terms used when talking about Flexbox dimensions and flow direction
举个例子会让这个理论更容易理解。清单 5-1 显示了这个例子的代码。你可以在图 5-3 中看到样本输出。我将在接下来的几页中解释这是怎么回事。
图 5-3。
The result of combining the HTML and CSS code shown in Listing 5-1
<section id=library"> <article class="library-item"> <h1>The Wonderful Wizard of Oz</h1> <h2>by L. Frank Baum</h2> <img src="img/oz.jpg" alt="Original cover of The Wonderful Wizard of Oz" /> <button>Remove from library</button> </article> <article class="library-item"> <h1>Pride and Prejudice</h1> <h2>by Jane Austen</h2> <img src="img/pandp.jpg" alt="Pride and Prejudice book cover" /> <button>Remove from library</button> </article> <article class="library-item"> <h1>Adventures of Huckleberry Finn</h1> <h2>by Mark Twain</h2> <img src="img/huck.jpg" alt="Original cover of Huckleberry Finn" /> <button>Remove from library</button> </article> </section> <style> #library { position: relative; display: flex; flex-flow: row wrap; } .library-item { display: flex; flex-flow: column; } .library-item > img { order: -1; align-self: center; } .library-item > button { margin-top: auto; } </style> Listing 5-1.HTML Markup Being Styled with Flexbox CSS PropertiesNote
需要一些额外的非 Flexbox 代码来定义图 5-3 中所示的颜色、边框和印刷样式。
让我们来看看这个例子的不同柠檬。
Flex 容器
flex 容器是根据 flex 布局的规则和属性放置 flex 项目的元素。Flex 项目是 flex 容器的直接子对象。每个 flex 容器可以包含零个或多个 flex 项目,这些项目可以是显式元素,如
<div>、<img>、<section>和<article>,也可以是一串连续的文本,Flexbox 将其视为包含在一个元素中。通过使用设置为值
flex或inline-flex的display属性,将元素定义为 flex 容器。display: flex和display: inline-flex的区别在于display: flex将容器定义为 CSS 2.1 块级项目,而display: inline-flex将容器设置为内联级元素。清单 5-2 显示了将#library部分定义为具有自己的 flex 上下文的 flex 容器的代码。#library { display: flex; } Listing 5-2.Defining the Library Element as a Flex Container by Assigning display: flex;当您使用
display: flex或display: inline-flex定义一个 flex 容器时,您正在为该容器的内容创建一个新的 flex 格式上下文。该上下文仅影响该容器的 flex item 子元素。外部元素不会影响嵌套的弹性项目。在flex的渲染中缺乏对外部元素的感知意味着float和clear对 flex 项目没有影响。还值得注意的是,多列布局模块中的列属性对 flex 项目没有影响。Flex 格式上下文
每个 flex 格式上下文都独立于其在布局中的对应部分工作。因此,对于创建完美的网格来说,Flexbox 并不是一个好的选择,在本书的后面,当我讨论一个专门为网格布局设计的模块时,你会看到这一点。这是因为单个容器允许它们的 flex 项目根据它们的大小和内容扩展或收缩,而不引用相邻 flex 容器中的 flex 项目。相比之下,基于网格的布局严格遵循特定的节拍和节奏,并考虑到相邻的元素。图 5-4 展示了三种不同的 flex 容器,每种容器都有自己的 flex 格式上下文,不关注相邻的容器。
图 5-4。
Flex items are sized and positioned relative to their own flex container, not flex items inside other flex containers
展示模型
像 flex 容器一样,flex 项目定义了自己的格式上下文。这可以使用
display属性和任何 CSS 2.1 允许的值来设置。因此,您可以将伸缩项设置为float,显示inline,或者设置为table-cell。如果使用显示值flex,flex 项本身就变成了一个 flex 容器,并支持其他属性,如visibility: collapse,这将在本章后面讨论。Note
如果您使用
position: absolute或position: fixed,flex 项将从 flex 格式上下文流中取出,除非left和right或top和bottom都设置了值auto。在这种情况下,这些位置属性的值是根据 flex 容器上下文中 flex 项目的静态位置计算的。在下一个例子中,主 flex 容器是一个
<section>元素,其id为library。每个子元素——一个具有library-item类的<article>元素——也被设置为使用display: flex。清单 5-3 隔离相关的 CSS。.library-item { display: flex; flex-flow: column; } Listing 5-3.CSS to Define Each Flex Item in the Context of the #library Flex Container每个 flex 项目实际上都是一个 flex 容器,如图 5-5 所示。因此,每个条目的内容也被视为 flex 条目,但仅在父条目
library-item的上下文中。图 5-5。
Each flex item in the
#libraryflex container acts as its own flex container, following a horizontal flex direction弯曲方向
flex-direction用于定义一个柔性容器的主轴。回想一下,灵活盒子布局是一个二维布局模块,因此只有两个轴可供选择:row:英文横。column:英文竖着。
每个轴可以向前或向后运行。定义
flex-direction: row反向版本的语法是flex-direction: row-reverse;而对于flex-direction: column,是flex-direction: column-reverse。Caution
术语
row和column可能会让你分别想到水平和垂直布局,但在 Flexbox 中,这只适用于水平书写模式。在垂直语言中,如日语,row 从上到下排列内容。默认主轴以及
flex-direction值是根据网页上使用的书写模式设置的。对于英语来说,这是从左到右,从上到下,定义为ltr。结果是使用ltr书写模式创建的网页默认为row,并且从左到右运行。设置值row-reverse使伸缩项从右向左显示。柔性包装
属性控制 flex 容器是单行的还是多行的。有三种可能的值:
nowrap:将 flex 容器定义为单行。容器中的所有 flex 项目都适合一个线性运行,而不需要换行到两行或更多行。这是flex-wrap的默认值。wrap:允许伸缩项沿横轴分布在两条或多条线上。wrap-reverse:类似wrap工作,但运行方向与默认相反。
如果通过使用值
wrap或wrap-reverse允许 flex 容器跨多行呈现,当没有足够的空间在一行中显示所有项目时,flex 项目会换行到第二行(或第三、第四或第五行)。对于响应式设计来说,这是一个非常有用的选项,因为它会根据容器自动对内容重新分页。与
flex-direction一样,默认的flex-wrap方向由写入模式定义。对于英语,这是从左到右,从上到下。在某些语言中,可能是从右到左,从上到下,或从上到下,从左到右。为了避免疑问,使用 HTML 元素上的lang和dir属性显式设置页面的语言和书写模式,如下所示:<html lang="en" dir="ltr">柔性流
flex-flow为flex-direction和flex-wrap属性提供了方便的快捷方式。它允许您使用一行代码来定义这两个属性,但是它也可以在没有flex-wrap值的情况下使用,就像flex-direction一样工作。清单 5-4 使用了flex-flow,尽管因为nowrap是flex-wrap的默认值,我可以省略第二个值。最终布局如图 5-6 左侧所示;右侧显示了如果我指定了wrap的话布局会是什么样子。图 5-6。
Notice the difference between the
nowrapandwrapvalues forflow-wrap.library-item { display: flex; flex-flow: column nowrap; /* nowrap is the default value for flex-wrap, so isn't strictly necessary here */ } Listing 5-4. Flex-flow Shorthand Solution for Defining Both the flex-direction and flex-wrap Properties控制项目的顺序
Flexbox 最好的特性之一是能够使用纯 CSS 控制 flex 项目的顺序。这是允许网页设计者正确区分风格和结构的一个重大进步,也给搜索引擎优化带来了好处。
默认情况下,伸缩项沿主轴排列。通过使用
order属性,您可以覆盖缺省值并指定特定项目在流中的位置。Order取整数值,较低的值呈现在较高的值之前。负值也是允许的,如清单 5-5 所示。.library-item > img { order: -1; align-self: center; } Listing 5-5.Negative Value for the order Property, Forcing the Image to Render at the Beginning of the Flow Along the Main Axisy让我们修改这个 CSS 代码来看看
order属性的作用。清单 5-6 用粗体显示了变化。图 5-7 按照单个元素的顺序显示了结果。图 5-7。
By adding specific
orderproperty values to eachlibrary-item, you can reorder them along the main axis without having to change the markup<style> #library { position: relative; display: flex; flex-flow: row wrap; } .library-item { display: flex; flex-flow: column; } .library-item:nth-child(1) { order: 3; } .library-item:nth-child(2) { order: 1; } .library-item:nth-child(3) { order: 2; } .library-item > img { order: -1; align-self: center; } .library-item > button { margin-top: auto; } </style> Listing 5-6.CSS Applied to the HTML Code in Listing 5-1, Updated to Include order Properties for the library-item ElementsNote
order属性只影响可视媒体,因此屏幕阅读器继续按照内容在标记中出现的顺序来阅读内容。在检查页面布局的可访问性时,理解这一点很重要。控制柔性
也许 Flexbox 规范中最重要的部分是定义 flex 项目的大小和间距的能力。传统上,很难在一个轴上排列导航项目,以使它们以相等的间距扩展或收缩来适应主尺寸。Flexbox 通过以下
flex-属性提供对这两个设计方面的完全控制:flex-growflex-shrinkflex-basis
这三个属性的组合可以很好地控制一个项目在没有足够的空间时是增长以填充空间还是收缩以适应空间,这是增长和收缩的基础。您马上会看到一个示例,但是让我们先依次看一下每个属性。
灵活增长
flex-grow属性定义了相对于同一 flex 容器上下文中的其他 flex 项,flex 项增长了多少。该值被指定为整数,默认为 1。因为该属性与上下文中的其他项目相关,如果您为一个特定项目设置值 2,则任何额外的空间都将被分割,这样,对于分配给其他 flex 项目的每 10 个像素的额外空间,flex-grow值为 2 的项目将获得 20px 的额外空间。属性flex-grow的值0防止任何可用的额外空间被给予灵活项目。弯曲收缩
flex-shrink类似于flex-grow,但是决定了当 flex 项目收缩以适合 flex 容器时如何分配空间。同样,该值是一个整数,默认值为 1,并且相对于容器中的其他 flex 项。当缺少可用空间时,flex-shrink属性的值0防止伸缩项收缩。弹性基础
flex-basis设置 flex 项目的初始宽度,但也可以设置为值auto,这允许浏览器根据项目的内容计算宽度。当flex-basis设置为正值或auto时,flex-grow和flex-shrink的基准都设置为内容周围的间距。(参见图 5-8 。)当flex-basis的值设置为0时,flex-shrink和flex-grow对项目占用的总空间进行操作。(参见图 5-9 。)图 5-9。
When
flex-basisis set with a value ofautoor a width greater than0, only the extra space is distributed图 5-8。
When
flex-basisis set with a value of0, all the space is evenly distributedflex 速记属性及其特例
您可以使用
flex属性作为flex-grow、flex-shrink和flex-basis的简写。对于flex有一些关于默认值和省略值的特殊规则,以说明 W3C 期望设计者使用该属性的最常见方式。这些很快就会显示出来以供快速参考,但是如果您有任何疑问,请确保在使用flex属性时指定这三个值中的每一个。清单 5-7 显示了
flex属性的语法。属性设置等同于列表后显示的单个属性。// Syntax flex: <flex-grow> <flex-shrink> <flex-basis> flex: 1 1 auto; // This is short-hand for each of following property:value pairs flex-grow: 1; flex-shrink: 1; flex-basis: auto; Listing 5-7. flex-grow, flex-shrink, flex-basis, and the Shorthand flex Properties三个
flex-属性的组合有几种常见的使用场景,因此规范定义了一些特殊的规则,当省略了flex速记语法的单个元素或者对其应用了文本值时,可以应用这些规则。这些是从 W3C 候选推荐标准中直接分解出来的:flex: 0 auto或flex: initial:相当于flex: 0 1 auto,也是该属性的默认值。它根据 CSS 中定义的width和height属性来调整 flex 项的大小。如果项目的主轴大小设置为auto,则项目的大小基于其内容。即使 flex 容器中有可用空间,该值也可以防止 flex 项目增长,但当空间不足时,它可以缩小到最小大小。flex: auto:这个跟flex: 1 1 auto一样。该值根据 CSS 中定义的width和height属性来调整 flex 项目的大小,但允许它完全灵活地扩展和收缩,以适应可用空间的大小。flex: none:相当于flex: 0 0 auto。当根据 CSS 中定义的width和height属性调整大小时,该值创建一个不灵活的 flex 项目。它类似于flex: initial,但不允许项目收缩。flex: <positive-number>:这个跟flex: <positive-number> 1 0px一样。结果是一个flex-basis为 0 的灵活伸缩项,根据容器中不同伸缩项上定义的伸缩因子来分配总空闲空间的一部分。
flex 的一个例子
flex属性为整个 Flexbox 模块提供了一个最令人兴奋的设计机会。现在,创建既有响应性又成比例的灵活布局变得很简单。清单 5-8 展示了这种灵活性。(你会在本章末尾看到一个更深入的例子。)<style> .layout { display: flex; flex-flow: row nowrap; } section > aside { flex: auto; } section > article { flex: 2 1 auto; } aside.level1 { order: -1; } aside.level2 { order: 2; } </style> <section class="layout"> <article class="maincontent"> ... </article> <aside class="level1">...</aside> <aside class="level2">...</aside> </section> Listing 5-8.HTML Markup and CSS to Which the Flexbox Properties Are Being Applied应用于清单 5-8 中标记的 CSS 将主要内容
<article>设置为占据两倍于<aside>元素的空间。所有的元素都会根据容器的大小而增减,内容也会重新排序,形成一个典型的三列布局。这种常见的布局方式如图 5-10 所示;在 Flexbox 之前,它需要浮动元素和特定的 HTML 顺序。图 5-10。
The result of the combination of HTML code and CSS shown in Listing 5-8
控制弹性项目的对齐
扩展和收缩 flex 项目以及沿主轴和横轴对齐和调整其大小的能力是 Flexbox 最有用的方面之一。使用 CSS,您第一次可以准确地定义项目在容器中应该如何对齐,还可以确定项目之间的间距。尽管已经有了脚本解决方法,但直到现在还不可能创建一个灵活的、水平居中的导航栏。更令人印象深刻的是垂直对齐项目(或横轴)的能力。
主轴
在 Flexbox 中有两种不同的方法来控制沿主轴的对齐:通过
margin和justify-content属性。边缘
边距在 flex 项目上的工作方式与 CSS 2.1 边距在块级元素上的工作方式非常相似。如果您设置了自动边距,flex 容器中的任何可用空间都会被分配给该轴上的边距。因此,通过将
margin-left: auto指定给一个 flex 项,你将它推到容器的右边(见图 5-11 ),而margin-right: auto将该项推到左边。以这种方式使用 margin 还可以防止项目增长以填充可用空间,因为所有空间都被 margin 占用了。清单 5-9 借用了 W3C 候选推荐规范中的一个例子来说明如何使用边距将一个列表项推到导航栏的右侧。图 5-11。
This output is achieved with Listing 5-9
<style> nav > ul { display: flex; } nav > ul > #login { margin-left: auto; } </style> <nav> <ul> <li><a href=/about>About</a> <li><a href=/projects>Projects</a> <li><a href=/interact>Interact</a> <li id='login'><a href=/login>Login</a> </ul> </nav> Listing 5-9.Using margin to Align One Item to the Main End Edge调整内容
Justify-content控制沿主轴分配给 flex 容器上下文中的 flex 项目的对齐类型。计算完任何一个margin或flex后,应用justify-content属性。这对于指定了最大弹性项目宽度或项目在某种程度上不灵活的布局非常有用。Justify-content分配剩余的可用空间。justify-content有五种不同的可能值:flex-start从 flex 容器的主起始边缘对齐项目。如果您的 flex 流从右到左水平运行,这意味着 flex 项目与容器的左侧对齐。flex-end从柔性容器的主要末端边缘对齐项目。如果您的 flex 流从右到左水平运行,这意味着 flex 项目将与容器的右侧对齐。center特别令人兴奋,因为它允许项目与 flex 容器的中心对齐,自动考虑所有 flex 项目占据的总主轴空间以及它们之间的任何间距。在灵活的盒子布局出现之前,这是不可能用 CSS 单独实现的。space-between沿主轴均匀分布柔性项目,第一个项目与主起点对齐,最后一个项目与主终点对齐。注意,如果没有足够的空间容纳所有的 flex 项,这个值的行为与flex-start相同。space-around与space-between相似,但在第一个项目之前和最后一个项目之后增加了一半大小的空间。剩余的项目再次平均分布在第一个和最后一个项目之间。
图 5-12 展示了这五个选项。
图 5-12。
These alignment options are available with
justify-content在多条生产线上工作
允许 flex 容器换行的一个好处是它给你的布局带来了灵活性。例如,如果你使用 Flexbox 导航条,你可以允许单独的导航选项溢出到第二行,如果有太多的导航选项在一行上放不下的话。这将自动使你的导航栏响应不同的屏幕尺寸。
如图 5-13 所示,最终结果可能有点不美观。但是,正如您所看到的,您可以使用
flex属性来使各个导航项目占据空间。如果你给弹性项目添加一个flex: auto规则,最终结果如图 5-14 所示更加漂亮。图 5-14。
The same navigation bar with
flex: autoapplied to the flex items图 5-13。
A navigation bar with
flex-wrap: wrapenabled横轴对齐
除了沿主轴对齐的能力之外,Flexbox 还提供了对横轴上 flex 项目对齐的控制。您不仅可以对齐,还可以自动拉伸项目,使它们在横轴上占据相同的空间,解决了基于浮动的布局多年来一直难以解决的问题。
有三个属性可用于控制横轴对齐:
align-itemsalign-selfalign-content
这些属性中的每一个都行使不同类型的控制,所以让我们依次来看看它们。
对齐项目和对齐自身
align-items和 align-self 的工作方式与justify-content类似,但它们是沿着横轴而不是主轴工作的。align-items适用于 flex 容器,而您可以在单个 flex 项目上使用 align-self。与justify-content一样,这些属性是在应用任何余量后计算的。以下是
align-items和align-self的可能值:flex-start和flex-end的工作方式与您预期的完全一样,分别将项目对齐十字起点或十字终点边缘。center根据 flex 容器沿横轴的总尺寸,沿横轴中心对齐项目。如果容器被设置为使用wrap或wrap-reverse的flex-flow值,允许多行,则项目沿着它们出现的行的中心对齐。baseline将每个项目的基线与十字起始边缘对齐。stretch沿横轴扩展项目以填充线条。这具有使每个伸缩项沿横轴占据相同空间的效果。如果您的 flex 项目设置了min-height/min-width或max-height/max-width,这些值仍然适用于这些项目,可能会导致项目无法填充行或溢出行。stretch是align-items的默认值。Auto是align-self的默认值。
图 5-15 显示了所有不同的选项和最终布局。
图 5-15。
These are the alignment options for
align-itemsandalign-self对齐内容
align-content 属性的工作方式与
justify-content类似,但它作用于启用了wrap或wrap-reverse的 flex 容器中的行,决定如何在这些行之间分配额外的空间。与justify-content一样,有几种不同的可能值,如图 5-16 所示:图 5-16。
Different
align-contentoptions are available for distributing and aligning individual lines in the flex containerflex-start从柔性容器的十字起始边缘对齐线条。如果您的 flex 流从右向左水平延伸,这意味着线条与容器顶部对齐。flex-end从柔性容器的交叉端边缘对齐项目。如果您的 flex 流从右向左水平延伸,这意味着线条与容器底部对齐。center将所有行对齐横轴上 flex 容器的中心,自动考虑所有行占据的总空间以及它们之间的任何间距。space-between沿十字轴均匀分布线条,第一条线条对齐十字线起点,最后一条线条对齐十字线终点边缘。注意,如果没有足够的空间容纳所有的行,这个值的行为与flex-start相同。space-around在第一行之前和最后一行之后添加半个空格。剩余的线再次平均分布在第一和最后一行之间。space-around类似于justify-content。- 拉伸会导致线条自动拉伸,并填充任何额外的可用空间。如果没有足够的空间来容纳所有的行,这个值同样呈现给
flex-start。
Note
align-content仅适用于具有多行的柔性容器。这是因为单行容器的行自动填充分配给容器的整个空间。折叠的项目
您可以通过为
visibility属性指定值collapse来折叠 flex 项目的可见性。这具有将 flex 项目从页面呈现中移除的效果,同时将它保留在格式化结构中。这允许折叠的项目定义 flex 容器横轴的整体比例,同时从视图中隐藏。这个新选项对于用户界面元素(如下拉导航菜单)特别有用,在下拉导航菜单中,只有顶级选项才会显示,直到用户选择一个项目,子项才会显示。通过继续影响横轴比例,您可以根据菜单中的最大选项自动设置菜单大小,即使这是折叠的。清单 5-10 完美地展示了这种行为。图 5-17 显示结果。
图 5-17。
The navigation menu with the second option opened to reveal the submenu. The widest submenu item sets the width of the overall menu container, despite being initially hidden
<style> nav > ul > li { display: flex; flex-flow: column; } /* dynamically collapse submenus when not targeted */ nav > ul > li:not(:target):not(:hover) > ul { visibility: collapse; } </style> <nav> <ul> <li id="nav-about"><a href="#nav-about">About</a> ... <li id="nav-projects"><a href="#nav-projects">Projects</a> <ul> <li><a href="...">Art</a> <li><a href="...">Architecture</a> <li><a href="...">Music</a> </ul> <li id="nav-interact"><a href="#nav-interact">Interact</a> ... </ul> </nav> Listing 5-10.Example from the W3C Specification, Showing a Dynamic Menu that Collapses the Visibility of Submenu Items如何使用灵活的方盒子布局
到目前为止,您应该对 Flexbox 的功能和用途有了清晰的认识。它可以单独解决许多常见的布局问题,因此很容易将 Flexbox 视为所有布局需求的答案。我想阻止你屈服于这种诱惑。尽管 Flexible Box Layout 能够呈现整个页面布局,但其他布局模块是专门为整个页面布局设计的(参见这一章之前和之后的章节,了解一些很好的选项!).
与整体页面布局相比,Flexbox 更适合单个用户界面元素。一些常见的使用场景包括:
- 希望在两个轴上真正居中的元素
- 需要呈现数量未知的项目的场景,例如通过内容管理系统控制的菜单
- 您希望根据标记顺序对内容进行重新排序的页面区域(尽管其他一些布局模块也允许您这样做)
- 隐藏当前未选择的内容的标签和内容群组
- 表单和表单元素布局
当然,没有什么可以阻止你使用 Flexbox 作为你的主要布局工具,但是考虑一下最适合手头任务的布局模块是值得的。
我不能放过一个关于 Flexbox 的章节而不提供一个 Flexbox 的实际例子。下面的示例只是一个可能的使用场景,它结合了您在前面几页中看到的许多属性。如果你还不相信灵活的盒子布局模块的能力和灵活性,我敢肯定这个例子会说服你。
真实世界的例子
这个例子为一个虚构的房地产公司创建了一个页面设计模型,如图 5-18 所示。该示例使用 Flexbox 来呈现几个布局部分。
图 5-18。
A mockup for a fictional real-estate company
HTML 标记
这个页面需要的 HTML 标记是基本的。它遵循的模式类似于布局设计者过去使用浮动来排列设计元素时使用的模式。您可以使用 Flexbox 来创建整个页面,但是因为最好将每个模块用于其预期目的,所以本示例将重点放在页面的以下部分:
- 导航栏
- 大屏幕区域
- 利益陈述
清单 5-11 显示了页面这些部分的相关 HTML 代码。
<!—The navigation section --> <nav> <ul> <li><a href="#">Home</a></li> <li><a href="#">Locations</a></li> <li><a href="#">Financing</a></li> <li><a href="#">Special Offers</a></li> <li><a href="#">About us</a></li> <li><a href="#">Contact Us</a></li> <li class="searchform"><form><input type="text" value="search" /></form></li> </ul> </nav> <!—The big icons/jumbotron section --> <section id="jumbotron"> <article> <h2>Free Advice</h2> <p>All our impartial advice is offered completely free of charge</p> <img src="img/bigicon-freeadvice.png" /> </article> <article> <h2>Discounted Removals</h2> <p>Once you've found your dream... ...</article> </section> <!—The badge benefits section --> <section id="benefits"> <article> <h1> Looking for a beautiful new home that won't break the bank?</h1> <p> Nulla vitae elit libero, a pharetra augue. Nulla vitae elit libero, a pharetra augue. Cras mattis consectetur purus sit amet fermentum.</p> </article> <article class="badge"> <div> <h3>Quality without compromise</h3> <p>We have homes that suit every budget without compromising on quality</p> </div> <img src="img/badge-quality.png" /> </article> <article class="badge">... ...</article> </section> Listing 5-11.HTML Code for Three Parts of the Page Suited to Flexbox Layout虽然使用 CSS2.1 可以实现这种布局,但使用 Flexbox,在页面上正确排列项目几乎变得微不足道。让我们单独看一下每一部分,这样你就可以看到 Flexbox 的布局是多么强大。
Note
与许多 CSS3 属性一样,在实现阶段,浏览器供应商会给属性添加前缀。我将向您展示不带前缀的代码,以保持清单的整洁。在支持完成之前,您需要使用每个属性的供应商前缀版本来完全支持每个浏览器。
航行
导航区域遵循您在清单 5-9 中看到的相同设计模式,因此这一部分的代码几乎相同。清单 5-12 显示了实现实体模型中显示的导航布局所需的所有代码!
/* The navigation section */ nav > ul { display: flex; flex-flow: row wrap; } nav > ul > .searchform { margin-left: auto; } Listing 5-12.Flexbox CSS Code to Create the Navigation Layout这段代码中的两个简单规则创建了将搜索表单推到导航区域右侧所需的布局。你可以看到图 5-19 中的输出,它是在 Chrome 30 中渲染的。
图 5-19。
The navigation output in Chrome
大屏幕
大屏幕区域(带有大图标)也很容易成形。Flexbox 可以帮助解决该领域正在进行的几件事情,例如:
- 每个
<article>大小均匀。 - 大屏幕中每个元素的内容在两个轴上居中对齐。
- 图像在文本之前呈现,但出现在标记中的文本之后。
为了对这些内容重新排序,您需要将每篇文章呈现为整个 flex 容器中的一个 flex 容器,id 为
#jumbotron。参见清单 5-13 。/* The jumbotron section */ #jumbotron { display: flex; flex-flow: row wrap; align-content: stretch; justify-content: center; } #jumobtron article { display: flex; flex-flow: column nowrap; flex: 1 1 250px; align-content: center; justify-content: center; } #jumbotron article * { align-self: center; } #jumbotron article img { order: -1; flex: none; } Listing 5-13.Flexbox Code to Style the Jumbotron Section图 5-20 显示了结果,用 Chrome 渲染。注意
<article>元素的flex的250px的底部宽度。这确保了所有的<article>以相同的宽度开始,并均匀地弯曲。这也意味着当使用 960 像素的窗口时,每行可以水平放置三篇文章。图 5-20。
When the window is wider than 960px, three
<article>s render per row当窗口大小折叠或在移动设备上查看页面时会发生什么?Flexbox 的一个主要优点是它可以作为一个响应式设计工具。这段代码足够灵活,可以处理不同大小的窗口。当窗口变得更窄时,内容会重新分页,这样每行只出现两个
<article>,如图 5-21 所示。图 5-21。
When the window narrows, the content automatically reformats onto more lines
当以智能手机的分辨率观看内容时,也会发生同样的事情。您可以通过将浏览器窗口拖至尽可能小的宽度来模拟这种效果。在这种情况下,一旦窗口窄于 500px,内容将再次重新格式化,每行显示一个
<article>(参见图 5-22 )。图 5-22。
At smartphone resolutions the content renders a single
<article>per line福利区
福利区域稍微复杂一些,因为左侧部分的高度是各个徽章区域的两倍。这种布局包含嵌套的 flex 容器,但是列而不是行充当主轴。这使得可以将左侧物品上的弯曲设置为徽章弯曲的两倍。参见清单 5-14 和图 5-23 中的 Chrome 输出。
图 5-23。
The output in Chrome
/* The badge benefits section */ #benefits { display: flex; flex-flow: column wrap; height: 260px; } #benefits article { flex: 2 2 260px; width: 318px; } #benefits article.badge { display: flex; flex-flow: row nowrap; flex: 1 1 130px; } #benefits article.badge img { order: -1; flex: none; } Listing 5-14.CSS Code for the Benefits Area注意文章被设置为使用
flex: 2 2 260px,而article.badge使用flex: 1 1 130px。第二条规则覆盖了第一条规则,并强制所有徽章的大小保持一致。剩下的文章正好是徽章高度的两倍,其flex-grow和flex-shrink值为 2,而徽章值为 1。这使得<article>占据了徽章高度的两倍。Note
需要一些额外的非 Flexbox CSS 代码来定义如图 5-18 到 5-23 所示的颜色、边框和印刷样式。
摘要
CSS 灵活的盒子布局提供了一个非常多才多艺的布局模型,非常适合创建响应用户界面元素。该模块使用基于轴的范例,它的内容可以根据通过
flex-属性集定义的一些简单规则沿着轴伸缩。Flexbox 解决了许多布局问题,到目前为止,这些问题都需要复杂的解决方案。在一个容器中集中排列多个项目现在变得很简单,就像对齐和调整盒子大小以使它们相互匹配一样。
浏览器支持很好,所以在网上使用 Flexbox 相当安全。大多数情况下,不支持的浏览器会自动退回到块级布局。您可以使用诸如 Modernizr 之类的库,这使得使用旧的 CSS 2.1 规范为这些浏览器提供多填充样式集变得很容易。