CSS 布局模块

560 查看

在众多浏览器刚刚支持 CSS 的时候,我就已经开始使用它了,并且应该算是最早采用 CSS 进行页面布局的开发者之一了。那时候,浏览器之间的兼容性虽然不好,但我仍然热衷于尝试层出不穷的新特性。最近几年在 CSS 领域,我们看到了许多重大进展。其中,web 字体、渐变、阴影和媒体查询已经成为了我们日常工作流的必备工具。

然而,CSS 布局却发展缓慢。开发者们曾经尝试使用 display:table 和 display:inline-block 来布局,用来缓解绝对定位和浮动布局所带来的束缚。然而,这些方法并不标准,因此它们又产生了新的问题。

CSS 布局的未来看起来一片大好。在这篇文章中,我将会介绍 CSS 规范中一些激动人心的布局模块。在未来,我们可以更有效地实现网格布局,更轻松地创建等高列或者均匀分配内容到整个页面。类似 Adobe 的公司往往熟悉布局设计的细节,借助它们的帮助来制定相关规范,我们就能更准确地控制页面在浏览器上的显示方式,同时避免对页面内容的影响。

对于本文中示例,我已经在一个或多个浏览器上进行了测试,当然你也可以继续测试它们。这些布局模块中的一部分可能还处于发展的初期阶段,其具体实现未来可能还会有变化,所以,你也可以就相关问题反馈给制定标准的团队。这是我们的 web,我们应该热衷于参与到制定规范的工作中。

如果你在线上产品中使用本书中的这些技巧,那么你要确保页面对低版本浏览器用户同样是友好的——即使这些低版本浏览器并不支持相关的布局模块。虽然我不想花过多的时间解决浏览器的兼容性问题,但在每一章节的最后,我会给你一些有用的建议和提示。

浏览器前缀

本文所涉及到的大部分属性,都会需要多个浏览器前缀。对于那些规范比较稳定的布局模块,比如多列布局和 flexbox 布局,我会使用 Lea Verou 的 -prefix-free 脚本,从而只需要演示标准属性,也可以实现跨浏览器的兼容性。对于线上产品,我建议你要么为 CSS 添加浏览器前缀,要么使用 CSS 预处理器将浏览器前缀编译到最终的 CSS 文件中。

对于那些非常新的布局模块(只被个别浏览器所支持,相关规范频繁变动的布局模块),我会为其添加所测试浏览器的前缀。在本书出版之时,通过添加特定浏览器前缀,其他浏览器可能也已经支持相关布局模块了。鉴于这些布局模块的实验性特征,所以在不同浏览器上可能会有不同的渲染结果。应该尽可能地为其他浏览器使用前缀并做相关的测试,最后在线上产品中还要加上无前缀的相关属性。

多列布局

CSS3 的多列布局已经风靡多年,然而,由于 IE 的不支持,它并没有获得预期的受欢迎程度。在 IE10 支持这些特性之后,它对于响应式设计就显得更为有用了——非常期待它能流行起来。多列布局模块是本书提到的所有模块中,技术最成熟、浏览器支持度最高的一个模块,所以从它开始讲述 CSS 中的新布局模块是一个不错的选择。

多列布局使内容均匀分散到多列成为可能,它非常类似于报纸中的“内容流动”效果。首先,你需要在文档中选择一个容器(container),然后声明该容器具有多列布局,那么浏览器就能实现预期的多列效果。如果你为内容(content)指定了列数,浏览器就会自动计算出每一列的宽度,自动适应父级元素的尺寸;如果你指定了每一列的宽度,那么浏览器就会自动计算列数,当父级元素尺寸发生变化,浏览器还可以自动重新计算。

示例:设置 column-width查看示例

设置 column-width 属性意味着要求浏览器尽可能多地为容器创建纵列(columns),而开发者指定的宽度则会被视为理想宽度。你可能已经注意到了,当我们指定单列的宽度时,实际上并没有得到预期的宽度。多列之间往往填充了空白(space),这有助于浏览器根据指定的宽度计算最合适的列数。CSS 多列规范中是这样解释的:

column-width的值指定了最理想的单列宽度。实际的单列宽度可能会更宽一些(多列之间填充了空白),或更窄一些(只有可用宽度小于指定宽度时才会出现这种情况)。该属性的指定值必须大于 0。”

因此,当需要设定单列宽度时,你只需指定一个理想宽度即可,因为为了设计的灵活性,实际宽度可能会出现一些差异。

示例:设置 column-count查看示例

你也可以使用 column-count 指定所需要的列数,然后让浏览器自行决定单列宽度。

调节间距

在上面的例子中,你会发现多列之间并没有紧紧相邻:这是因为多列之间存在间距(gap)。在多列布局中,间距是由column-gap 属性控制的。如果将 column-gap 设为 0,那么多列之间就不会有间距,所有的文字会拥挤在一起。规范中建议浏览器在默认样式中为该属性预设 1em 的宽度。不过,如果让多列的间距保持一致性,那么你应该显式地为该属性设定一个值。

示例:设置 column-gap 属性查看示例

美化多列布局

在最新的规范中已经限制了对多列布局的美化。不过,相关的工作草案也提示说:“或许在未来的规范中会添加额外的功能。比如,允许多列中的任意一列设置不同的宽度和背景。”目前来说,你还不能单独地美化多列中的某一列。

虽然没法设置单列的内外边距、宽度和背景色,但是我们可以使用一些规则来隔离多列。实现这一布局效果需要使用column-rule 属性:

  • column-rule-style
  • column-rule-width
  • column-rule-color

这些属性的用法非常类似 border-styleborder-width 和 border-color,而且也可以使用 column-rule 属性作为一种简写形式:

上面的纵列属性(column rules)将会被应用到 column-gap 上。要想修改纵列两边的空白间距,就需要调整column-gap 的属性值。如果上述纵列属性的值大于可用间距,那么它就会与文本区重叠——但它不会占用任何的空间。

示例:使用 column-rule查看示例

单列跨度(span)

如果你想让某一个元素延伸到所有的纵列中,那么可以为该元素添加 column-span 并赋值为 all。在下面的示例中,我要让 h1 标题可以延伸到所有的纵列上。

示例:强制 h1 延伸到多列查看示例

当前的规范中 column-span 只有两个值:all 和 none

多列截断

当使用多列布局的时候,你需要控制多列截断的方式。如果不希望某些元素被截断到新的纵列,或者确保某个元素固定在某一列时,下面的这个属性对你就会很有帮助。

示例:避免在段落内和引用块之前被截断查看示例

印刷和分页媒体类型

规范指出,多列布局内部的元素不应该出现在下一页上。如果阅读时翻到了下一页,然后让用户再返回前一页继续阅读,那么这样的体验就太让人恼火了。

你可以预设分页时段落或者元素内部内容的布局方式——就像控制多列截断一样。属性 avoid-page 和 avoid-column 就可以帮助你实现良好的控制。如果你允许段落进行截断而不允许内容分页,那么对于上面的例子,就可以使用 break-inside: avoid-page 替代 break-inside: avoid;

响应式设计

由于多列默认就是响应式的,所以多列布局也有助于实现响应式设计。正如我们所知,虽然可以根据需求设定单列宽度,但本质上浏览器会使用自己的算法,计算出一个用于渲染的宽度。

单列内的图片会被限制在单列范围之内,所以使用 max-width 设置的最大宽度也被局限在单列之内。如果你没有为图片设置 max-width: 100%;,同时图片的宽度还大于单列宽度,那么浏览器就会自动裁剪掉多余的部分。该规则同样适用于其他宽度大于单列宽度的元素。

示例:图片宽度限制于单列之内查看示例

一定不要认为多列布局模块只适用于创建类似报纸的版式。下面示例中的多列布局,就包含了一系列的盒模型和图片。

示例:盒模型和图片查看示例

下面的示例演示浏览器视口较窄时的单列布局,无需编写额外的代码即可实现。

浏览器提示

当我写下这些文字的时候,还只有几个浏览器支持多列布局。因此,有时你需要添加浏览器前缀才能使用这些属性,此外,在有一些情况下某些属性并不会生效。更多更新的浏览器支持信息,可以查看 Can I Use。如果浏览器不支持多列布局,那么它在解析样式表时就会忽略相关属性,因此可以放心使用这些属性。不支持多列布局的浏览器会将内容渲染为单列,这种渲染结果在大多数情况下也是可以接受的——不建议使用腻子脚本模拟多列布局效果。

CSS Flexbox布局

CSS 弹性盒布局模块,通常被称为 flexbox,为我们提供了一种新的布局——flex 布局。设计该布局的初衷是为了简化复杂应用和页面的布局代码。在本节中,我将会着重介绍一些使用 flexbox 解决的布局问题。

均匀排列

在传统的布局设计中,将一组布局元素沿坐标轴均匀排列是件很麻烦的工作。如果使用浮动布局,那么每个浮动元素都必须设置一个宽度,否则就会宽窄不一,此外,往往需要使用 JavaScript,才能让所有的浮动元素均匀排列在同一行上。

Flexbox 极大简化了这一布局过程。在下面的示例中,我使用无序列表创建了一个导航条。

示例:flexbox 简单用法查看示例