05月15, 2017

CSS 可视化格式模型概述

译者按:可视化格式模型可以说是 CSS 规范中最最重要的一部分。要精通 CSS 布局,这一部分内容必须掌握透彻。个人认为 CSS 高手和菜鸟的区别就在于:在布局时,菜鸟在布局时候用什么属性基本都是很盲目,随意下手,出现问题就去网上找解决方案或者瞎试,就算最后解决了,也不知道为什么,下次依然会犯错;而熟悉规范这部分的高手写一个属性就知道会对其它部分产生什么影响,所以犯错会很少,开发效率也高。

本文是 CSS Mastery 第三版的第三章的翻译。


浮动、定位和盒模型是需要掌握的一些最重要的 CSS 概念。这些概念控制了元素在页面上安排和显示的方式,并形成了很多布局技术的基础。最近,专门设计用来控制布局的新标准已经提出了,我们会在之后的章节中分别看到这些标准。不过,本章所学的概念会帮助你完全掌握盒模型的难点、绝对定位和相对定位之间的区别、以及浮动和清除浮动的工作原理。一旦牢固掌握了这些基础知识,使用 CSS 开发网站就会变得容易得多。本章中将学习如下内容:

  • 盒模型的难点
  • 外边距如何折叠以及为什么会折叠
  • 不同的定位属性和值
  • 浮动和清除的工作机制
  • 什么是格式化上下文

盒模型简介

盒模型是 CSS 的基石之一,规定元素如何显示,在某种程度上,也规定了元素之间如何交互。页面上每个元素被当作是一个矩形盒子,由元素的内容区(Content Area)、内边距(padding)、边框(border)和外边距(margin)组成(参见图 3-1)。

alt

图 3-1 盒模型示意图

内边距应用在内容区周围。如果给元素添加背景,它会应用到由内容和内边距组成的区域。因此,内边距经常被用于在内容周围创建一个隔离带,这样内容就不会与背景混在一起。添加边框会在内边距区外面加上一条线。这些线会以不同的风格出现,比如实线、点线或者虚线。边框之外是外边距。外边距是盒子可见部分之外的透明空间,让我们可以控制页面中元素之间的距离。

另一个可以应用到盒子,但是不能影响其布局的属性是 outline 属性,该属性会在元素的边框盒周围画一条线。它不会影响到盒子的宽度或高度,在调试复杂的布局或者演示布局效果时很有用。

内边距、边框和外边距都是可选的,默认都是零。不过,很多元素会被用户代理样式表设置了外边距和内边距。例如,正文标题默认总是会有点外边距,虽然这些会根据浏览器而有所不同。当然,你可以在自己的样式表中覆盖这些浏览器样式,要么是在特定元素上,要么是采用 重置样式表,正如第二章所讨论的那样。

Box-sizing

默认情况下,一个盒子的 widthheight 属性指内容盒的宽度和高度,即组成元素被渲染内容边缘的矩形。添加边框和内边距不会影响内容盒的大小,不过会增加整个元素盒的大小。如果想让一个每一边有 5px 边框和 5px 内边距的盒子达到总宽度 100px,就需要把内容的宽度设置为 80px,如下所示。如果盒子周边还有 10px 的外边距,它就会占据总共 120px 的宽度(参见图 3-2)。

.mybox { 
    width: 80px; 
    padding: 5px; 
    border: 5px solid; 
    margin: 10px; 
}

alt

图 3-2 默认的盒模型。width 属性应用到内容区

我们可以通过使用 box-sizing 属性,改变盒子宽度计算的方式。box-sizing 的默认值是 content-box,适用于迄今为止所描述的行为。不过,如果能让 widthheight 属性不仅仅只影响内容盒的话,那就是很有用的了,特别是在响应式布局中。

注意:有些表单控件元素(比如 input)在某些浏览器中可能会有不同的 box-sizing 默认值。这是由于要与遗留的行为保持兼容性,而遗留的行为是不能修改像内边距或者边框这些东西。

如果像如下所示那样,将 box-sizing 属性设置为 border-box,那么 widthheight 属性就会包含盒子的内边距和边框所需的空间(参见图 3-3)。外边距依然影响元素在页面上占据的整体大小,不过依然不会被包括进 width 给定的尺寸中。我们可以用如下的规则实现如图 3-2 所示的相同整体布局。

.mybox { 
    box-sizing: border-box; 
    width: 100px; 
    padding: 5px; 
    border: 5px; 
    margin: 10px; 
}

alt

图 3-3. box-sizing 属性被设置为 border-box 时的盒模型。width 属性现在与元素可见部分的整体宽度有关。

那么为什么这个是有用的呢?从很多方面来说,这种处理盒子的方式更直观,而且它实际上是 IE6 之前的老版本 IE 中盒模型的工作方式。之所以说它“直观”,是因为当我们考虑它时,这是盒子在现实世界中的工作方式。

假如把一个 CSS 盒子当作是包装箱。把箱子的墙当作是边框,提供视觉定义,而内部的填充物(padding)保护内容。如果盒子必须是指定的宽度,那么添加更多的填充物,或者增加墙的厚度,会侵蚀可用的内容空间。现在如果在把盒子堆起来之前要把盒子隔开,每个盒子之间的距离(实际上就是外边距)对盒子本身的宽度或者可用内容空间的实际数量没有影响。这种解决方案感觉更合理一些,所以遗憾的是浏览器开发者,包括微软在 IE 后续版本中,决定走不同的路。

幸运的是,box-sizing 属性允许我们覆盖掉默认的行为,简化 CSS 布局中的一些常见模式。看看如下示例:

<div class="group"> 
    <article class="block"> </article> 
</div>

如果我们想确保 .group 内的所有 .block 的宽度总是包含它的列的三分之一宽度,可以应用如下的样式规则:

.group .block {
    width: 33.3333%; 
}

这段规则是没问题的,不过如果我们开始在 .block 的周边使用外边距来添加隔离带,让内容离可见的边缘远一点的话,就会出问题。现在 .block 元素是其父元素 .group 的宽度加上外边距的三分之一,这样潜在会破坏想要的布局。图 3-4 阐述了这种不同。

alt

图 3-4. 假设我们想让 .block 元素是 .group 元素的三分之一,在给它添加外边距时,会得到意想不到的结果

我们可以解决这个问题,比如,通过给要添加外边距的元素添加一个额外的内部元素,或者选用一个不同的 box-sizing 属性来改变宽度计算的方式(参见图 3-5):

.group .block { 
    width: 33.3333%; 
    box-sizing: border-box; 
    padding: 20px; 
}

alt

图 3-5. 添加 box-sizing: border-box,让盒子保持 33.3333% 宽,即使添加外边距也不会受影响

现在 .block 元素就刚好是父元素宽度的三分之一,跟我们声明的亿阳,不管我们给它添加多少外边距或者边框。

内边距、边框和外边距可以应用到一个元素的所有边上,或者单个边上。外边距还可以是负值。负外边距可以用很多有趣的方式将元素从页面位置拉进拉出。在后面章节我们会探究一些这些技术中。

我们可以使用 CSS 规范中任何长度单位(比如 px、ems 以及百分比),给元素添加内边距和外边距。使用百分比值有一定的特殊性,值得一提。假设标记与前一个例子相同,那么在本例中,5% 实际表示多少呢?

.block {
    margin-left: 5%; 
}

答案是,本例中,它是父元素 .group 宽度的 5%。假如 .group 元素是 100px 宽,那么它就会有 5px 的左外边距。

当涉及到在一个元素的顶边以及底边对内边距或者外边距使用这些单位时,你情有可原会猜测百分比是从父元素高度的派生出来的。这在开始似乎是合乎逻辑的,不过,因为通常不会声明高度,并且这个高度可能与内容的宽度有很大出入,所以,CSS 规范规定内边距和外边距的上部和下部值也会采用包含块(containing block)的宽度。在本例中,包含块是父元素,不过这是可以改变的 - 我们会在本章稍后一点来解决这意味着什么。

最小值和最大值

有时给元素应用 min-width 和 max-width 属性可能会很有用。这样做,在实践响应式设计时尤其有用,因为它允许块级盒默认自动填满其父元素的宽度,而不会收缩到比 min-width 中指定的值更小,或者扩展到比 max-width 中指定的值更大。(我们会在第八章回到响应式网页设计以及它与 CSS 如何关联。)

同样,也有 min-height 和 max-height 属性。不过在 CSS 中应用任何高度值时,都应该小心,因为元素差不多总是隐式地从它们所包含的内容中推导出其高度。否则,如果内容量增长了,或者文本大小改变了,内容就会从固定高度的盒子溢出来。如果因为某些原因,确实需要设置默认的高度尺寸,那么使用 min-height 通常会好一点,因为它让盒子可以扩展其内容。

可视化格式模型

理解了盒模型之后,我们就可以开始探索一些可视化格式模型以及定位模型了。

人们通常会成像 ph1article 这类元素为块级元素。意思是这些元素都是可以在视觉上显示为内容块,或者块盒。相反,像 strongspantime 这些元素被描述为行内级元素,因为它们的内容是在行内显示为行内盒。

通过使用 display 属性,可以改变生成的盒子的类型。这意味着通过将一个行内级元素(比如 span)的 display 属性设置为 block,可以让一个行内级元素表现得像块级元素。也可以通过将一个元素的 display 属性设置为 none,导致该元素完全不生成盒子,盒子及其所有内容不再在文档中显示出来,也不会占据空间。

在 CSS 中有几种不同的定位模型,包括浮动、绝对定位和相对定位。除非特别指明,否则所有盒子都是在常规流中定位,并且默认属性是 static。顾名思义,常规流中一个元素的盒子的位置是由 HTML 中该元素的位置所决定的。

块级元素会一个接着一个垂直出现;盒子之间的垂直距离是通过盒子的垂直外边距计算出来的。

行内盒(inline box)是随着文本流,在一行内水平排列,并且当文本换行时会换行到新的一行。其水平间距可以使用水平方向上的内边距、边框和外边距进行调节(参见图 3-6)。不过,垂直方向上的内边距、边框和外边距对行内盒的高度没有影响。同样,在行内盒上设置一个显式高度或者宽度也没有效果。

在水平方向上由一行文本组成的盒子称为行盒(line box),并且行盒的高度总是足以容纳它所包含的所有行内盒。改变行盒大小的唯一方法是通过改变行高,或者在行盒内的任何行内盒上设置水平方向上的边框、内边距或者外边距。图 3-6 展示了一个段落的块盒,带有两行文本,其中单词之一是在一个显式为行内的 <strong> 元素内。

alt

图 3-6. 一个段落块盒内的行内组件

我们还可以将一个元素的 display 属性设置为 inline-block。顾名思义,这样的声明让元素水平排列,就好像它是一个行内盒一样。不过,盒子的内部表现的就像盒子是块级一样,包括能显式地设置宽度、高度、垂直外边距和内边距。

当使用表格相关的标记时(table、tr、th 以及 td 元素等等),table 本身是表现为块,但是表格的内容会根据生成的行和列排成行。还可以设置其它元素的 display 属性,这样它们就能采用表格的布局行为。通过以正确的方式应用值 table、table-row 和 table-cell,我们就可以达到 HTML 表格的一些属性,而不需要在标记中使用表格。

在后面章节中会讲解的像 Flexible Box Layout(也称弹性盒)以及 Grid Layout 这些模块已经更进一步扩展了 display 属性。通常,这些新的布局模式会创建内部上下文中充当块的盒子,但是会为如何对待盒子内的内容创建新的规则。

这种外部和内部显示模式的区分(遍布 inline-block 和 table,以及新的值,比如 flex 或者 grid)现在已经被在 Display Level 3 模块中标准化了。这里,针对显式模式的已有属性和关键字已经被扩展了,从而允许更细粒度的控制。要点是,行内级盒和块级盒依然是 HTML 元素默认行为的基础,不过现实是更加微妙。

匿名盒

与 HTML 元素可以嵌套一样,盒子也可以包含其它盒子。大多数盒子由显式定义的元素构成。不过,有一种情况是即使没有显式定义,也会创建了一个块级元素 - 即在块级元素的开头添加一些文本时,比如如下所示的 section。即使没有把 some text 定义为块级元素,它也会被当作是。

<section>
    some text
    <p>Some more text</p>
</section>

在这种情况下,这个盒子被称为匿名块盒(anonymous block box),因为它不与特地定义的元素关联。

块级元素内的行盒文本也有同类的事情。假设有一个段落包含有三行文本。每行文本就形成了一个匿名行盒。对于匿名块盒或者匿名行盒,我们是不能直接设置样式的,除非是通过使用 :first-line 伪元素选择器。当然,这种使用也是有限制的,只允许改变某些与字体和颜色相关的属性。不过,它有助于理解在屏幕上所看到的一切都会创建某种形式的盒子。

外边距合并

提到常规的块盒,这里有一种行为,称为外边距合并。外边距折叠相对来说是一个简单概念。不过,在实际中,在对网页布局时,它会添不少乱。简单来说,当两到多个垂直外边距相遇时,它们会合并成一个外边距。合并后的外边距的高度会等于两个被合并的外边距中较大的哪一个的高度。

当两个元素是一个在另一个之上时,第一个元素的下外边距会与第二元素的上外边距合并(参见图 3-7)。

alt

图 3-7. 一个元素的上外边距与前一个元素的下外边距合并

当一个元素被包含在另一个元素之内时,假如没有内边距或者边框分开外边距,那么它们的上下外边距也会合并在一起(参见图 3-8)。

alt

图 3-8. 一个元素的上外边距与其父元素的上外边距合并

看起来更奇怪的是,外边距甚至自己都可以合并。假如有一个空元素,有外边距,但是没有边框和内边距。此时,上外边距就接触到下外边距,二者就合并在一起了(参见图 3-9)。

alt

图 3-9. 一个元素的上外边距与其下外边距合并

如果这个合并后外边距与另一个元素的外边距接触,又会合并(参见图 3-10)。

alt

图 3-10. 一个空元素的合并后的外边距与另一个空元素的外边距合并

这就是为什么一串空段落元素只会占据很小的空间,因为所有其外边距都合并到一起,形成一个小的外边距。

外边距合并刚开始会看起来很奇怪,不过它实际上很有意义。假设有一个由几个段落组成的典型文本页(参见图 3-11)。第一个段落上的空间等于段落的上外边距。如果没有外边距合并,所有后续段落之间的间距会是两个相邻上外边距和下外边距之和。这意味着段落之间的间距将是页面顶部空间的两倍。有了外边距合并,每个段落之间的上外边距和下外边距就会合并,从而让间距和其它地方相同。

alt

图 3-11. 外边距合并维护元素之间的一致间距

外边距合并只发生在文档常规流中块盒的垂直外边距上。行内盒、浮动盒以及绝对定位盒子之间的外边距永远不会合并。

包含块

元素的包含块(Containing block)的概念很重要,因为它决定了各种属性如何解释,比如之前我们看到过的内边距和外边距用百分比设置的例子。

元素的包含块取决于元素是如何定位的。如果元素是静态定位(与没有声明 position 属性一样)或者相对定位,其包含块被计算为 display 属性设为会导致块状上下文的 block、inline-block、table-cell、list-item 等值的最近的父元素的边缘。

默认情况下,当 width、height、margin 和 padding 是用百分比设置时,其具体值是从这个父元素的尺寸计算得来的。当将元素变为绝对定位或者固定定位时,计算规则就会改变。接下来我们会学习不同的定位模式,以及它们如何与包含块的概念相互作用。

相对定位

当将一个元素的 position 属性设置为 relative 时,它最初会待在原地。之后我们可以通过使用 top、right、bottom 和 left 属性设置一个垂直或者水平位置,从而将元素相对于其起点移动。如果将 top 位置设置为 20px,盒子就会出现在原始位置顶部之下 20px 的地方。将 left 位置设置为 20px,如下所示,会在元素左边创建一个 20px 的空间,将元素向右移动(参见图 3-12)。

.mybox { 
    position: relative; 
    left: 20px; 
    top: 20px; 
}

alt

图 3-12. 相对定位一个元素

使用相对定位,元素会继续占据页面流中原来的空间,不管是偏移与否。同时,偏移元素可以导致它与其它盒子重叠。

绝对定位

相对定位实际上被看作是常规流定位模型的一部分,因为元素是相对于它在常规流中的位置。与之相反,绝对定位是把元素从文档流中脱离出来,因此不占据空间。文档常规流中的其它元素会把绝对定位元素当作是从来就不在那儿一样(参见图 3-13)。

alt

图 3-13. 绝对定位一个元素

绝对定位元素的包含块是其最近的定位祖先,即 position 属性不为 static 的任何祖先元素。如果该元素没有定位祖先,就会相当于文档根元素(即 html 元素)定位。这也被称为初始包含块(the initial containing block)。

与相对定位盒一样,绝对定位盒也可以从其包含块的上下左右偏移。这就给了我们很大的灵活性。我们可以直接把元素定位到页面的任何地方。

因为绝对定位盒是从文档流中脱离出来,所以可以覆盖页面上的其它元素。我们可以通过设置z-index 属性,来控制这些盒子的堆叠顺序。z-index 越高,盒子在栈中的位置就越高。在用 z-index 堆叠东西时,有各种错综复杂的东西要考虑:我们会在第六章中把它们搞清楚。

虽然绝对定位对于页面元素的布局是一个有用的工具,不过它极少用于创建高层布局。绝对定位盒子不参与文档流的事实,让它很难创建布局来适应和应对不同宽度和不同内容长度的视口。Web 的性质就不会轻易让我们为页面上元素所在的位置指定精确的尺寸。随着我们对 CSS 中的其它布局技术越来越精通,绝对定位的使用对页面布局已经变得相当罕见。

固定定位

固定定位是绝对定位的子类,不同之处在于固定定位元素的包含块是视口。它让我们可以创建一直停留在窗口相同位置的浮动的元素。很多网站使用这种技术,让导航部分固定在侧栏或者顶栏定位,从而让导航总是看得到(图 3-14)。这有助于改善可用性,因为用户就不需要为回到界面的重要部分找太远。

alt

图 3-14. Google 开发者文档的顶栏和侧导航在滚动时保持不动

浮动

另一种重要的可视化模型是浮动模型。浮动盒可以向左或者向右移动,直到其外部边缘碰到包含块或者另一个浮动盒的边缘。因为浮动盒不在文档的常规流中,所以文档常规流中的块盒就几乎表现得好像浮动元素不在那儿一样。我们马上会解释“几乎”是什么意思。

如图 3-15 所示,当把盒子1 向右浮动时,它从文档流中脱离出来,向右移动,直到它的右边缘碰到包含块的右边缘。它的宽度还会收缩到包含其内容所需的最小宽度,除非你已经通过设置一个特别的宽度或者 min-width/max-width,明确告诉它。

alt

图 3-15. 元素向右浮动的示例

在图 3-16 中,当把盒子1 向左浮动时,它从文档流中脱离出来,向左移动,直到其左边缘碰到包含块的左边缘。因为它不再在文档流中,所以它不占空间,实际上在盒子2 之上,让盒子2 在视图中消失了。如果它所有三个盒子都向左浮动,盒子1 移动左边,直到它碰到其包含块,而其它两个盒子也向左移动,直到碰到前一个浮动盒。

alt

图 3-16. 被向左浮动的元素示例

如果包含块太窄,不能在水平方向上容纳所有浮动元素,那么余下的浮动会向下移动,直到有足够的空间(参见图 3-17)。如果浮动元素的高度不同,那么在向下移动时可能被其它浮动卡住。

alt

图 3-17. 如果没有足够的水平可用空间,浮动元素会向下移动,直到有足够空间

行盒和清除

在前一节我们学习到,浮动一个元素,会把它从文档流中脱离出来,不再对非浮动的东西产生影响。实际上,这种说法并不是很严格。如果一个浮动元素后跟一个在文档流中的元素,那么该元素的盒子会表现得好像浮动不存在一样。不过,文本内容的盒子会保留对浮动元素的一些记忆,会让开,以腾出空间。用技术术语来说,紧接着浮动元素的行盒会缩短,为浮动元素腾出空间,从而环绕着浮动元素。实际上,当初创建浮动就是为了让文本可以环绕图像(参见图 3-18)。

alt

图 3-18. 行盒在紧挨着浮动时候会缩短

如果要阻止行盒环绕在一个浮动盒外面,需要给包含这些行盒的元素应用一个 clear 属性。clear 属性的值可以是 left、right、both 或者 none,它指示盒子的哪一个边不会挨着浮动盒。很多人认为 clear 属性只是删除了一些标志,消除了之前的浮动。不过,现实更有趣。当清除一个元素时,浏览器会给该元素的顶部添加足够的外边距,将该元素的上边框边缘垂直向下推,直到越过浮动(参见图 3-19)。这有时会让人困惑,比如说当试着将自己的外边距应用到“被清除了的” 元素上时,因为这个值会没有效果,直到它达到以及超过浏览器自动添加的外边距值为止。

alt

图 3-19. 清除元素的上外边距为之前的浮动创建足够的垂直空间

正如你所见,浮动元素从文档流中脱离出来,对周围的元素没有影响,除了会缩短行盒,为浮动盒腾出足够的空间以外。不过,清除一个元素本质上是为所有先前的浮动元素清除出一个垂直空间。在使用浮动作为布局工具时,这是很有用的,因为它允许周围的元素为浮动元素留出空间。

下面我们来看看如何用浮动来创建一个简单的组件布局。假如我们想让一个图片浮动到一个标题的左边,让一小块文本浮动向右浮动,这通常称为“多媒体对象”,因为它常见的模式就是一块多媒体(比如图示、图像或者视频)以及一块附文。我们想让这个图片和文本被包含在另一个有背景颜色和边框的元素中。可以想如下这样试试:

.media-block { 
    background-color: gray; 
    border: solid 1px black; 
} 
.media-fig { 
    float: left; 
    width: 30%; /* leaves 70% for the text */ 
} 
.media-body { 
    float: right; 
    width: 65%; /* a bit of "air" left on the side */ 
}
<div class="media-block"> 
    <img class="media-fig" src="/img/pic.jpg" alt="The pic" /> 
    <div class="media-body"> 
        <h3>Title of this</h3> 
        <p>Brief description of this</p> 
    </div> 
</div>

不过,因为浮动元素是从文档流中脱离出来,所以 class 值为 .media-block 的容器 div 不占空间,因为它只包含有浮动内容,从而没有什么能给它一个在文档流中的高度。那么如何让容器在视觉上围住浮动元素呢?我们需要在该元素内部某个地方应用一个清除,这样就正如我们前面看到的,会在被清除浮动的元素上创建足够的垂直外边距,以容纳被浮动的元素(参见图 3-20)。不幸的是,在示例中并没有已有元素要清除,所以我们可以在 div 关闭标记之前添加一个空元素,然后清除这个空元素:

/* 添加的 CSS: */ 
.clear {
    clear: both; 
} 

<div class="media-block"> 
    <img class="media-fig" src="/img/pic.jpg " alt="The pic" /> 
    <div class="media-body"> 
        <h3>Title of this</h3> 
        <p>Brief description of this</p> 
    </div> 
    <div class="clear"></div><!-- 添加一个额外的空 div --> 
</div>

alt

图 3-20. 添加一个要被清除的 div,强制容器围住浮动

这样就得到我们想要的结果,但是是以给标记添加了无关代码为代价。通常会有一个已经存在的元素可以应用清除浮动,不过有时我们不得不忍痛为布局而添加无意义的标记。不过,在这种情况下,我们能做得更好一些。

方法是按照如下所示,使用 :after 伪元素模拟一个特殊的清除元素。通过将它应用到浮动元素的包含元素,就会创建一个特殊的盒子,这样就可以将清除规则应用到这个盒子上面。

.media-block:after { 
    content: " "; 
    display: block; 
    clear: both; 
}

这种方法及其变种被 Nicholas Gallagher 通过小代码片段最好地演示了。Nicholas Gallagher 称之为 微 clearfix,放在 http://nicolasgallagher.com/micro-clearfix-hack/。

格式化上下文

CSS 有若干不同的规则集应用于当元素在页面上垂直或者水平排列时,元素之间如何相互作用。这些规则集之一的技术名是格式化上下文(formatting context)。我们已经看到过了行内格式化上下文(inline formatting context,简称 IFC)的一些规则,比如,垂直外边距对行内盒没有作用。同样,某些规则应用于块盒如何堆叠,就像我们在外边距合并一节所看到的那样。

其它规则定义页面是如何必须自动包含在末尾伸出的所有浮动(否则浮动元素内的内容会伸出可滚动区之外),以及所有的块盒默认情况下会让其边缘与其包含块的左边缘对齐(或者右边缘对齐,取决于文本方向)。这个规则集称为块格式化上下文(block formatting context,简称 BFC)。

有些规则允许元素在自己内部建立块格式化上下文。它们包括:

  • 元素的 display 属性值被设置为给元素的内容创建块状上下文,比如 inline-block 或者 table-cell。
  • float 属性值不为 none 的元素。
  • 绝对定位的元素。
  • overflow 属性值不为 visible 的元素。

正如我们先前所讨论的那样,规则说,一个块的边缘会碰到其包含块的边缘,这对前面有一个浮动元素的内容也适用。浮动元素从页面流中脱离出来,通过让跟在它后面的元素中的行盒变短,创建一种给它自己腾出空间的视觉效果。元素本身依然会在浮动下面延伸到它所需的空间为止。

当一个元素有规则会触发新的块格式化上下文,并且挨着一个浮动时,它会忽略必须将它的边缘挨着其包含块的一边的这条规则,而是会收缩到合适的大小 - 不仅仅是行盒收缩,而是整个盒子都收缩。这可以用来用更简单的规则来重建上一节中的 .media-block 示例:

.media-block { 
    background-color: gray; 
    border: solid 1px black; 
}

.media-fig { 
    float: left;
    margin-right: 5%; 
} 

.media-block, .media-body {
    overflow: auto; 
} 

<div class="media-block"> 
    <img class="media-fig" src="/img/pic.jpg" alt="The pic" /> 
    <div class="media-body"> 
        <h3>Title of this</h3> 
        <p>Brief description of this</p> 
    </div> 
</div>

通过在包含的 .media-block 和 our .media-body 都设置 overflow: auto;,我们为它们创建了一个新的块级格式化上下文。 这会有两个效果(参见图 3-21 中的比较):

  • 它在 .media-block 组件内包含了浮动图像,而不需要用清除规则,因为块级格式化上下文会自动包含浮动。
  • 一个额外的好处是,如果我们想的话,它允许我们抛弃 .media-body 元素上的宽度及浮动规则 -- 它会只调整浮动后面的剩余空间,并且依然会为浮动后面的图像保持一个不错的直边。如果没有新的格式化上下文,并且文本有点长,那么在浮动的 .media-fig 之下的所有行盒会伸展到它之下,直到与图像之下的左边对齐。

alt

图 3-21. 如果只有 .media-fig 元素是浮动的,并且文本足够长,有些行会在浮动元素之下换行,一直到左边结束。创建一个新的块格式化上下文会强制 .media-body 收缩

以尽可能可预测和简单的行为创建布局,会减少代码的复杂性,增加布局的健壮性。所以,知道什么时候用这种技巧,以避免浮动和清除元素之间的复杂交互,是件好事情。幸运的是,更好的布局技术正在迅速增长。

内在和外在的大小调整

CSS 模块 “Intrinsic and Extrinsic Sizing Level 3” 定义了可以应用到 (min- 和 max-)width 和 height 属性的一系列关键词值,而不是像素或者百分比长度等等。这些关键词表示的是显式长度,它们要么是周边的上下文(外在的)派生的,要么是元素的内容派生的(内在的),不过是让浏览器计算出最终的值。与隐式值形成对比,比如设置为 auto,或者完全不设置宽度,而使用浮动或者块级格式化上下文,来创建收缩到合适尺寸的情况。

这里我们不打算深入研究不同关键字,但是我们注意到一个有意思的事情是,在这些关键词中,我们发现了 contain-floats。这个关键词跟我们所期待的一样;例如,我们可以用如下代码,让一个元素包含任何浮动:

.myThing {
    min-height: contain-floats; 
}

迄今为止,这个模块中各种关键词的支持是很弱的,特别是在本书编写时,所有版本的 IE 都不支持这些关键词。不过,这些都是在将来很有用的,可以在不需要使用更多的技术情况下,用一个关键词就创建强大的大小调整。

其它 CSS 布局模块

我们已经覆盖了 CSS 可视化格式模型的基础知识以及最常用的部分,不过还有一些其它方面要简要介绍一下。

你可以想像一下,一个强大而灵活的布局模式会是像 CSS 这种可视化展现工具的关键部分。你是对的;不过不幸的是,我们花了很长时间才得到一个。过去我们已经用 CSS 中可用的随便啥功能实现我们的目标,即使它们远非这种工具的理想工具。最开始我们用 table 实现布局,因为它有布局的特点,尽管它有臃肿的标记和不恰当的语义。再后来,我们用浮动和绝对定位来实现大多数复杂页面布局,不过这两个功能都不是为网页布局而设计的。这两种方式都有严重的限制,只不过我们不得不忍受。

谢天谢地,最近的 CSS 模块已经引入了专门为创建灵活而强大的页面布局而设计的新内容模型。在本书编写之时,这些模块都处于不同的准备状态,并且有一些还没有可以操作的跨浏览器实现。在后续章节中我们会详细研究一下这些模块,以及它们所启用的一些更有用的技术,这里我们只是给出它们所提供的功能类型的简单总结。

弹性盒布局

我们之前已经遇到的 Flexible Box Layout Module(弹性盒布局模块,即 flexbox)是 CSS 3 中引入的一种布局模型。弹性盒允许我们将一个盒子的子盒要么在水平方向上布局,要么在垂直方向上布局,并且确定这些子盒的大小、间距和分布。它还允许我们修改元素在页面上渲染的次序,不管它们在 HTML 源代码中的位置。弹性盒作为常规流模型(inline 和 block)的一种升级,对有关内容本身以及它如何影响大小调整上,提供了在精确控制和灵活性之间的一种平衡。

弹性盒被广泛实现,但是较老版本的 IE 中的支持很明显确实或者不完整。好消息是它是以一种可以与其它方法(比如浮动)组合在一起的方式构造的,从而可以创建很强大的布局。我们会在第六章进一步研究弹性盒。

网格布局

网格布局(Grid Layout)是第一个完全成熟的 CSS 高级布局工具,其目的是取代历史上用浮动和定位元素创建的复杂布局。它将布局与源代码完全分离,并将网格系统的思想从各个模块的内容和表现结构中抽出来。其中,弹性盒(flexob)是“微观”,网格布局是“宏观”,所以这两种方法相辅相成。

在本书编写之时,网格布局尚未得到广泛支持,但是浏览器厂商正在努力实现它。我们将在第 7 章了解这个强大的新模块。

多列布局

多列布局模块(The Multi-column Layout Module)是一种让内容流入单个列的相当简单的方式;例如,创建类似报纸的布局,其段落的文本流入多个垂直列。

该模块允许我们选择设置的列数或首选宽度,根据可用空间留下要跟踪的列数。我们还可以控制列之间间隙的空间,给这些间隙应用类似边框一样的视觉效果。由于多列布局比一般布局更多是一种排版工具,我们将在第4章中讨论。

Regions

CSS Regions 指定内容如何在页面上的元素之间流动。一个元素作为内容的来源,但是该内容不是以常规块流动,而是可以流入页面其他地方的其他占位符元素。这意味着布局不再被 HTML 的源代码顺序所影响,从而让布局展示与内容的结构解耦。

CSS Regions 允许以前单独使用 CSS 时不可能实现的布局,并且可能会推动将来采用某些基于打印的布局模式。不过,很少有浏览器制造商对 CSS Regions 表现出任何的兴趣,而且这种类型的布局将不会成熟到足以使用一段时间的风险。因此,我们不会在本书中进一步详细介绍 Region。

总结

本章我们学习了盒模型,以及内边距、外边距、宽度和高度如何影响盒子的大小。还学习了外边距合并的概念,以及它如何影响布局。还被介绍了 CSS 的各种格式化模型,比如常规流、绝对定位和浮动。我们学习了行内盒和块盒之间的区别,如何对一个相对定位的祖先内的元素进行绝对定位,以及清除的工作原理。

现在我们掌握了这些基础知识,下面我们开始好好利用它们。在本书后面的章节中,将介绍一些核心CSS概念,我们将看到如何在各种有用和实用的技术中使用它们。所以启动你最喜欢的编辑器,开始写编码吧。

本文链接:http://www.xiaojichao.com/post/cssvisualformatting01.html

-- EOF --

Comments