前端开发者学堂 - fedev.cn

CSS 布局模块

发布于 南北

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

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

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

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

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

浏览器前缀

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

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

多列布局

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

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

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

.col-wrapper {
    column-width: 220px;
}

CSS布局模块

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

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

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

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

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

.col-wrapper { 
    column-count:3; 
} 

CSS布局模块

调节间距

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

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

.col-wrapper { 
    column-width: 220px; 
    column-gap: 1.5em; 
} 

美化多列布局

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

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

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

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

column-rule: [width] [style] [color]; 

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

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

CSS布局模块

单列跨度(span)

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

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

.col-wrapper h1 { 
    column-span: all; 
    padding: 0 0 .5em 0; 
}

当前的规范中 column-span 只有两个值:allnone

CSS布局模块

多列截断

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

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

.col-wrapper p { 
    break-inside: avoid; 
} 
.col-wrapper blockquote { 
    break-before: avoid; 
} 

印刷和分页媒体类型

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

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

响应式设计

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

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

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

CSS布局模块

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

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

CSS布局模块

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

CSS布局模块

浏览器提示

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

CSS Flexbox布局

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

均匀排列

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

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

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

<nav class="mainnav"> 
    <ul> 
        <li><a href="">Introductory</a></li> 
        <li><a href="">The First Cat Show</a></li> 
        <li><a href="">Habits</a></li>

        <li><a href="">Trained Cats</a></li> 
        <li><a href="">Usefulness of Cats</a></li> 
    </ul> 
</nav> 

对于这个导航条,我想实现水平均匀分布的效果。如果我们选择 flexbox 布局方式,那么只需要添加 display:flex 属性,并指定布局元素的排列方式(所有元素平均排列或者除首尾元素外平均排列)。

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

nav ul{
    margin: 0;
padding: 0;
list-style: none;
display: flex;
justify-content: space-between;
} 

CSS布局模块

在这里,我们将 justify-content 设置成了 space-around,这样做的好处就是让每一个元素之间具有相同的间距,避免了溢出容器的问题。此外,在第一个元素之前和最后一个元素之后,也添加了相同的间距。

CSS布局模块

上面的示例使用了一些 flexbox 的默认属性,比如这里的布局元素默认显示为水平排列。这个默认的排列效果等同于添加了 flex-direction: row 的效果。

flex-direction 属性一共拥有四个属性值:rowrow-reversecolumncolumn-reverse。使用这些属性,可以实现水平排列、反向水平排列、垂直排列和反向垂直排列四种布局效果。

nav ul {
    margin: 0;
    padding: 0;
    list-style: none;
    display: flex;
    justify-content: space-between;
    flex-direction: row-reverse;
}

CSS布局模块

使用 flexbox 的另一个优势在于,它可以帮你创建等高容器——即使容器内的文字不等长。align-items 的默认属性值为 stretch,它会根据 flexbox 内最高元素的高度,拉伸其他布局元素,使之等高。在我的导航示例中,如果你缩窄窗口,文字就会自适应为多行,但仍然保持等高拉伸的布局——从所有元素的边框就可以看出他们是等高的。

CSS布局模块

align-items 的所有属性值:

  • flex-start
  • flex-end
  • center
  • baseline
  • stretch

为了理解其正确的解析方式,你需要首先理解 flexbox 中的两个轴概念:主轴和侧轴,其中主轴用来控制元素的布局方向。通过将 flex-direction 设置为 row 或者 column,可以指定主轴,并确定布局元素的排列方向:从左到右或者从上到下;第二条轴,即侧轴,垂直于主轴。

如果将 flex-direction 设为 row,那么主轴方向就是从左到右的。设置 align-itemsflex-end,意味着布局元素不会占据布局容器的全部高度,所有的布局元素在底部对齐,不做等高处理。

CSS布局模块

多行 flexbox

在之前的这个示例中,如果我们将浏览器缩窄,那么布局元素内的文字就会在布局元素内拆分成多行。究其原因,是因为布局元素的内容宽度超过了可用空间。一种可行的解决方案就是将布局容器设为 wrap(表现为多行效果)。

在 flexbox 布局中,使用 flex-wrap 属性可以控制布局容器的多行模式,可用的属性值包括 wrapnowrapwrap-reverse。如果没有显式声明该属性值,那么 flex-box 默认为 nowrap

示例:布局容器的多行模式-查看示例

nav ul{ 
    margin: 0; 
    padding: 0; 
    list-style: none; 
    display: flex; 
    justify-content: space-between; align-items: stretch; flex-direction: row-reverse; 
    flex-wrap: wrap;
}

CSS布局模块

然而,从图中可以发现,相邻布局元素之间出现了意料之外的不规则空隙。修正这种表现的方式是为布局元素添加额外的布局间距。为布局元素设置 flex: auto; 即可实现这一目标——在我们的示例中,我为 li 元素添加了该属性。

nav li {
    border: 1px solid #999; 
    border-radius: 2px; 
    flex:auto;
    margin: 0 1em 1em 0; 
    text-align: center;
}

CSS布局模块

使用 flex 属性

虽然为布局元素设置 flex: auto; 可以调整它们在布局容器内的空间排列,但该属性的作用远不止如此,这里将会介绍几种其他的用法。

在下面的示例中,我编写了三个块级元素,块级元素内部是一些关于长毛猫品种的信息。此外,我还为块级元素添加了公有的类名 box 和独立的类名,便于选择特定的块级元素。

示例:三个块级元素-查看示例

<div class="boxes">
    <div class="box box1">
        <h2>The Angora</h2>
        <p>... </p> 
    </div>
    <div class="box box2"> 
        <h2>The Persian Cat</h2> 
        <p>... </p>
    </div>
    <div class="box box3">
        <h2>The Russian Long-haired Cat</h2>
        <p>... </p> 
    </div>
</div>

为了让所有的块级元素在单行内排列,我将布局容器,即 boxes,设为了 flex 元素,然后为容器内的每个元素设置 flex: 1。设置完成后,所有的布局元素就都具有了相同的宽度。

.boxes {
    display: flex;

    flex-direction: row;

    flex-wrap: wrap;

    align-items: stretch; 
    justify-content: space-between; 
} 
.box {
    border: 1px solid #999; 
    border-radius: 5px; 
    flex: 1;

    margin: 0 1em 1em 1em; 
    padding: 10px; 
} 

CSS布局模块

此外,我们可能需要让某个布局元素宽于其他布局元素,同时还要根据可用的容器空间计算各个布局元素的宽度。示例中的第三个块级元素拥有一个类名 box3,如果为 box3 添加 flex 属性并赋值为 2,那么相比于 flex: 1; 的布局元素,它就会具有两倍的宽度。

.box3 {
    flex: 2;
}

CSS布局模块

为 flex 布局元素排序

从前面的介绍中,我们已经了解到 flex 布局元素是可以反向排列的。实际上,我们可以为每个独立的布局元素设置任意的排列顺序,其中的关键,就需要用到下面介绍的 order 属性。

通过为每个布局元素设置 order 属性,我可以轻松移动 box3。只需要为其添加 order: 2;,就可以将这个最宽的布局元素移动到容器中央。

.box1 {
    order: 1;
}
.box2 {
    order: 3;
}
.box3 { 
    flex: 2; 
    order: 2; 
} 

CSS布局模块

虽然我们更改了渲染后的布局顺序,但实际上它的 HTML 结构仍旧保持不变。这意味着你可以根据实际需求来编写 HTML 结构,这样做有助于提高可用性,改善使用文本阅读器的用户体验。此外,使用该属性还可以创建出色的布局效果。

响应式设计

对于响应式设计,flexbox 是一种优秀的可选方案。由于它具有包裹多行、自适应可用空间的特点,可以让我们不费吹灰之力就创建出简单的响应式效果。

如果你在 flexbox 中混合媒体查询的功能,那么就可以创建出更复杂的布局效果。由于可以按照不同于源码的结构来显示布局元素,所以我们能够在不同尺寸的屏幕上创建不同的布局效果。此外,也可以像下面的示例一样,使用 flex-direction 来转换布局方向。最初,我们可以让导航显示为垂直排列,当窗口宽度大于 700px时,我们就可以将其转换为水平排列。

nav ul{
    margin: 0;
    padding: 0; 
    list-style: none; 
    display: flex; 
    flex-direction: column;
    justify-content: space-between;
    align-items: stretch; 
}
@media only screen and (min-width: 700px) { 
    nav ul {
        flex-direction: row; 
    }
}

浏览器提示

Flexbox 是一个极佳的案例,展示了 CSS 规范在初期的演变方式。由于 flexbox 的具体实现和最初构想之间已经发生了诸多变动,所以在 flexbox 的可用性上存在大量无效的资料。当你需要检索有关 flexbox 的资料时,可以查看一下 CSS Tricks 上的相关文章,便于检验当前资料的准确性。

当浏览器不支持 flexbox 时,一方面可以使用 JavaScript 来模拟 flexbox 的大量特性,另一方面你也可以让浏览器优雅降级,让布局元素呈现一种线性排列,下图就是之前的布局元素在浏览器不支持 flexbox 时的渲染效果。

CSS布局模块

如果你只是将 flexbox 布局模块应用于少量的界面元素,而不是整体布局,那么优雅降级为线性排列是比较简单的。另一种方式是使用 Modernizr 检测浏览器是否支持 flexbox,然后分别为支持和不支持 flexbox 的情况编写不同的 CSS 代码。

CSS 网格布局

CSS 网格布局模块是由微软提议的,目前仍在进行大量的规范化工作。该模块的最新进展已经应用到了 Internet Explorer 10 上,所以本部分的实例演示都会基于 IE10。

网格布局旨在解决复杂网页的布局问题——在该布局提出之前,我们只能通过元素的浮动和定位来模拟复杂布局。网格布局也允许开发者显示与源码结构不同的布局结构——类似上一章节的 flexbox 布局。我很喜欢网格布局,希望你在阅读完下面的示例之后也会喜欢它。

创建网格

将布局元素包裹进网格的第一步,就是在它们的父元素上创建网格。首先需要添加 display: grid;(通常我会加上 -ms- 前缀),然后设置所需的网格行数和列数。

示例:一个简单的网格布局-查看示例

.wrapper {
    display: -ms-grid;
    -ms-grid-columns: 200px 20px auto 20px 250px; 
    -ms-grid-rows: auto 1fr;
}

上面的 CSS 代码在 .class 元素上创建了网格,该网格拥有五个纵列:一个 200px 宽度的侧列,一个 20px 宽度的间距,一个自适应宽度的中间纵列,另一个 20px 宽度的间距,以及最后一个 250px 宽度的纵列。由此可见,这是由两个固定宽度的纵列和一个可变宽度的中间纵列构成的简单布局。

在横列上,我将第一列设为了 auto——它将根据内部内容自动扩展高度,将第二列设为了 1fr1fr 准确的说是一个分数比值,在这里意味着第二列在剩余空间所占的比重。由此可见,这是具有两个横列的布局。

现在,我们可以往网格里添加一些内容了。添加内容后的结构如下:

<div class="wrapper"> 
    <nav class="mainnav"> 
        <ul>
            <li><a href="">Introductory</a></li> 
            <li><a href="">The First Cat Show</a></li> 
            <li><a href="">Habits</a></li>
            <li><a href="">Trained Cats</a></li> 
            <li><a href="">Usefulness of Cats</a></li>
        </ul> 
    </nav>
    <h2 class="subhead">Usefulness of cats</h2> 
    <article class="content">
        <p>...</p>
    </article>
    <blockquote class="quote">
        <p>....</p> 
    </blockquote>
</div>

为了将导航定位到左侧固定宽度的纵列上,将文章部分定位到中间的纵列上,将引用定位到右边的纵列上,我又添加了如下的样式:

.mainnav { 
    -ms-grid-column: 1; 
    -ms-grid-row: 2;
}
.subhead { 
    -ms-grid-row: 1; 
    -ms-grid-column:3;
}
.content { 
    -ms-grid-column: 3; 
    -ms-grid-row: 2;
}
.quote { 
    -ms-grid-column: 5; 
    -ms-grid-row: 2;
}

上面所做的处理就是在指定具体的内容所对应的纵列。一定要牢记,间距也是纵列,所以主内容区的是在第三列上,最右侧的纵列是第五列。

CSS布局模块

当我尝试理解这种网格布局时,我发现将其设想为传统的表格布局会很有帮助,不同之处在于网格布局是在 CSS 中实现的。然后,我要做的是将布局元素放入表格单元,不过,不同于表格布局,为了适配不同尺寸的显示屏幕,在 CSS 中重新定义网格结构是更加方便的。这意味着在将来,网格布局会成为响应式设计强有力的工具。

网格和响应式设计

现在,我要在这里涉及一些复杂的布局——这些布局更具有实用性,展示网格布局在响应式设计中的实际应用。

示例中使用的文档结构很简单:一个添加了 wrapper 类名的 div 标签;五个添加了 box 类名的 div 标签,标签内添加了一些内容;以及一张关于猫的图片标签——图片来自维多利亚时代的书籍。为了简单起见,我给每个 box 元素都添加了第二个类名,便于在 CSS 中对其进行定位和检索。

示例:一个响应式的网格布局-查看示例

<h1 class="title">Extracts from "Our Cats, by Harrison Weir"</h1> 
<div class="wrapper">
    <div class="box content1"> 
        <h2>The first cat show</h2> 
        <p> ...</p>
    </div>
    <div class="box content2">
        <h3>The Angora</h3>
        <p>... </p> 
    </div>
    <div class="box content3"> 
        <h3>The Persian Cat</h3> 
        <p>... </p>
    </div>
    <div class="box content4">
        <h3>The Russian Long-haired Cat</h3> 
        <p>...</p>
    </div>
    <div class="box picturebox">
        <figure>
            <img src="fluffy.jpg" alt="Fluffy the cat" /> 
            <figcaption>Fluffy, the cat</figcaption>
        </figure>
    </div> 
</div>

只需要使用少量基础的 CSS 属性做一下美化,我们就得到了一个如下图所示的线性设计(linearised design)。

CSS布局模块

下面,需要定义网格了。我是通过给 .wrapper 添加相关的属性实现的:

.wrapper {
    width: 90%;
    margin: 0 auto 0 auto;
    display: -ms-grid;
    -ms-grid-columns: 1fr (4.25fr 1fr)[6]; 
    -ms-grid-rows: (auto 20px)[4];
}

通过设置 display: -ms-grid;,表示了该元素内部包含一个网格布局。通过使用一个简写形式的多列语法,我创建了多个纵列。将有关纵列的参数集合放入圆括号内,其后跟一个参数为数字的中括号,这种语法表示我们想要重复设定某种纵列类型:在这个示例中,这意味着我将这两个纵列重复了六次。纵列之间的间距被设定为 1 个分数单位,而纵列则拥有 4.75 个分数单位的宽度。

然后,我使用相同的语法设定了横列,创建了一个根据内容自适应高度的横列,其后跟着一个 20px 宽度的间隙,最后将上述的横列格式重复了四次。

如果添加完上述的 CSS 后刷新页面,你会发现所有的纵列都被折叠了。这是因为所有的布局元素都尝试去填充第一纵列和第一横列,我们需要进一步为布局元素设定定位信息。一旦你为某个元素声明了网格信息,那么所有的子元素就都需要在网格中进行定位。

CSS布局模块

为了适配小尺寸的屏幕,我将从元素定位开始做布局。由于用户使用小尺寸设备时是从上到下阅读的,所以我会按照源码中的内容顺序来布局,而对于使用屏幕阅读器的用户,我会根据内容的重要性来安排布局顺序。在分离源码结构和布局结构这个新领域,我无需过多担心视觉效果背后的源码结构。

.content1 {
    -ms-grid-row: 1; 
    -ms-grid-column: 2; 
    -ms-grid-column-span:12;
}
.content2 { 
    -ms-grid-row:3; 
    -ms-grid-column: 2; 
    -ms-grid-column-span:5;
}
.content3 { 
    -ms-grid-row:3; 
    -ms-grid-column:8; 
    -ms-grid-column-span:6;
}
.content4 { 
    -ms-grid-row:7; 
    -ms-grid-column: 2; 
    -ms-grid-column-span:12;
}
.picturebox { 
    -ms-grid-row:5; 
    -ms-grid-column: 2; 
    -ms-grid-column-span:12;
}

这些属性看起来都相当简单。现在,我需要选择具体的横列放入合适的内容。一定要牢记横列中间有 20px 的间距,所以我们应该将布局元素放入奇数横列中。然后,我为每个区域设置了起始纵列和纵列跨度。到此为止,大多数布局元素的宽度都是等于容器宽度的,但我将其中的 content2content3 分割成了两个纵列。现在,在浏览器中就可以显示出一个简单的布局了。

CSS布局模块

接着,我要添加第一个媒体查询了,让布局在浏览器窗口大于 700px 的宽度时发生一些改变。

@media only screen and (min-width: 700px) { 
    .wrapper {
        -ms-grid-columns: 1fr (4.25fr 1fr)[9];
        -ms-grid-rows: (auto 20px)[5]; 
    }
    .content1 {
        -ms-grid-row: 1; 
        -ms-grid-column: 2; 
        -ms-grid-column-span:17;
    }
    .content2 {
        -ms-grid-row:3; 
        -ms-grid-column:8; 
        -ms-grid-column-span:5;
    }
    .content3 {
        -ms-grid-row:3; 
        -ms-grid-column:14; 
        -ms-grid-column-span:5;
    }
    .content4 { 
        -ms-grid-row:3; 
        -ms-grid-column: 2; 
        -ms-grid-row-span: 3; 
        -ms-grid-column-span:5;
    }
    .picturebox {
        -ms-grid-row:5; 
        -ms-grid-column: 8; 
        -ms-grid-column-span:11;
    } 
}

对于较宽的浏览器窗口,我重新界定了网格,增加了更多的纵列和一个额外的横列。布局顶部的第一个内容区的宽度仍然占满了整个容器。不过,我将其他的内容去分隔成了三个纵列,其中一个内容区跨越两个横列,另外两个内容区只有前者高度的一半,并将图片放在了这两个内容区的下面。

CSS布局模块

最后,我使用媒体查询,为 940px 以及更宽的屏幕重新设计了网格布局。

@media only screen and (min-width: 940px) { 
    .wrapper {
        -ms-grid-columns: 1fr (4.25fr 1fr)[16];
        -ms-grid-rows: (auto 20px)[3]; }
    .content1 { 
        -ms-grid-row:1;
        -ms-grid-row-span: 3; 
        -ms-grid-column: 20; 
        -ms-grid-column-span:13;
    }
    .content4, .content2, .content3 {
        -ms-grid-row:1; 
    }
    .picturebox { 
        -ms-grid-row:3;
    } 
}

这次重新界定的网格是一个基于 960 网格系统的标准网格布局。现在,我创建的网格布局中,包含了三个窄小的纵列和一个较宽的纵列,图片被放在了较窄的两个纵列下面。

CSS布局模块

网格布局模块真正的魅力在于,我们无需改变源代码中的标记结构,即可轻松变换内容在布局中的位置。我认为该模块对于初次接触它的开发者也是通俗易懂的,尤其是对于那些和我一样还记得表格布局的老派开发者。

浏览器提示

正如本章开始所解释的,因为网格布局语法是由微软提出的,所以该模块目前只被 IE10 所支持。如果你正在开发 Windows 方面的应用程序,那么可能已经在相关的开发环境中用到它了。我也希望看到它被更多的浏览器所支持。

CSS Regions

本文的最后两章将会讨论一些由 Adobe 和微软提议的新方案,它们都很有意思。虽然这些新方案的发展比起网格布局来说还处于初级阶段,但它们可以创建一些不可思议的布局效果。此外,从探究 CSS 布局中的新特性这个角度来说,也是非常值得的。

CSS Regions 语法为内容流创建了一种高级模型。我们可以通过指定文档的特定区域,让该区域的内容实现“流动显示”的效果——即使指定的这些区域在文档结构中并不相邻。在 web 环境中,这听起来有些怪异,不过,这种布局常常被用于印刷设计,特别是杂志和报纸。

CSS Regions 并没有提出任何新的布局方法,所以可以使用现有的或将来会出现的方法来定位元素。

为了演示 CSS Regions 的使用方式,最简单的方法就是举例说明。在编写本书的时候,只有 Chrome Canary 版本的浏览器支持该布局,而且还要保证已经开启了 WebKit 的实验性特性。如果你有这样的浏览器,那么就可以把玩下面的示例代码了。

下面就是示例的 HTML 结构。我使用了一个 article 标签来包裹文章的所有内容,并在文档底部添加了多个空标签。

<div class="wrapper"> 
    <article class="main"> 
        <h2>Usefulness of cats</h2> 
        <p>...</p>
        <p>...</p>
    </article>
    <div class="region1 article-regions"></div> 
    <div class="regionwrapper">
        <div class="region2 article-regions"></div> 
        <div class="region3 article-regions"></div>
        <div class="region4 article-regions"></div> 
    </div>
    <div class="region5 article-regions"></div> </div>
</div>  

article 标签内包含了布局前的原始内容,而后面的空标签则充当了这些内容的容器。

首先,我需要为原始内容的容器添加一个新的 CSS 属性 flow-into(附加 -webkit 前缀),该属性的值 article-thread 是我为原始内容声明的一个名字,也可以用任何其他的名字。

.main { 
    -webkit-flow-into: article-thread; 
}

一旦添加了该属性,那么原始内容就会消失,这是因为你还没有指定它们的布局位置。

在上面的 HTML 结构中,每个空标签都拥有一个 article-regions 的类名,所以我可以让它们接收原始内容。

.article-regions { 
    -webkit-flow-from: article-thread; 
}

在这里,我们使用了一个新的属性 flow-from,并匹配了之前为原始内容声明的名字。如果现在刷新页面,就会发现内容重新出现在页面上了。接下来,就可以让第一个区域“流动起来了”。当内容“流动起来”之后,我们添加 CSS 来指定流动区域。

.region1 {
    height: 10em; 
 }
.regionwrapper {
    display: flex; 
    flex-direction: row;
}
.region2 {
    flex: 1;
    padding: 10px;
    height: 40em;
    border-right: 1px dotted #ccc;
}
.region3 {
    flex: 1;
    padding: 10px;
    height: 40em;
    border-right: 1px dotted #ccc; 
    background-color: #efefef;
}
.region4 {
    flex: 1; 
    padding: 10px; 
    height: 40em;
}
.region5 {
    padding: 1em 0 0 0; 
    margin: 1em 0 0 0;
    border-top: 1px dotted #ccc;
    height: auto; 
}

现在 region1 区域已经拥有了 10em 的高度。一旦内容超过了这个高度,内容就会尝试填充到其他地方,所以在这里它会流入下一个区域。接下来的三个区域通过使用 flexbox 布局,被定位到了三个弹性纵列中,所以内容可以“流”入这些区域。由于这三个区域是固定高度的,当内容充满它们之后,就会继续“流”入最后的区域,该区域具有自适应的高度,可以容纳剩余的所有内容。

CSS布局模块

如果我改变想法了,想要在内容中间插入其他的东西,那么我也是可以做到的。在下面的示例中,我从 flexbox 中间的纵列上移除了 artcle-regions 类名,所以内容就不会再“流”入其中。现在我可以为这个纵列添加一张有关猫的图片,并引用一句话——它们和周围“流动的内容”并没有多大关系。

CSS布局模块

浏览器提示

在本章,我们尝试了 CSS 模块中非常新颖的部分,这些部分目前只在 WebKit 浏览器开启实验性特性时被支持。虽然这并不是一个可以用于线上环境的技巧,但我认为 CSS Regions 所创造的无限可能是一件激动人心的事。

CSS Exclusions

如果你曾经需要用到文字环绕图形的效果,或者想要裁剪位于内容中央的图形,那么你可能会对 CSS Exclusions 和 Shapes 模块感兴趣。一个早期的、参考了浮动定位的规范,已经被当前的工作草案所取代。

我们都非常熟悉 CSS 中的浮动,关于浮动最简单的例子就是浮动图片被文字环绕的效果。不过,浮动在这种场景下是受到严格限制的。浮动元素总是出现在顶部,当我们将图片浮动到左侧时,文字就会环绕在它的右侧和下方,无法将图片置于文档的中央,实现文字的四周环绕效果,也无法将其浮动到底部,让文字环绕在上方和两侧。上面提到的,就是 Exclusions 和 Shapes 模块首要解决的问题,并已经在 IE10 和开启实验性特性的 Webkit 浏览器中获得了支持,下面我们将会看一个实例。

这个实例就是一段简单的文字,其外层被一个类名为 mainarticle 标签所包裹。在 article 标签之后,紧跟着一个 div 标签,标签内部是一张猫的图片。我希望这张图片在布局时呈现一种文字环绕的效果。

<div class="wrapper"> 
    <article class="main">
        <h2>Usefulness of cats</h2> 
        <p>...</p>
    </article>
    <div class="exclusion">
        <img src="fluffy.jpg" alt="Fluffy the cat" />
    </div> 
</div>

首先,我要像之前一样为它创建一个网格。与浮动不同的是,Exclusion 需要被定位(因此在浮动定位前需要有个名字)。我是使用 IE10 演示该示例的,所以可以使用网格布局来实现这种定位。

我创建的是一个两行三列的网格,并在创建后让 article 布局范围覆盖到了所有的行列上。虽然 div 标签在源码结构中是紧跟在 article 标签之后的,但它会显示在文字的上方。

.main {
    -ms-grid-column: 1; 
    -ms-grid-row: 1; 
    -ms-grid-row-span: 3; 
    -ms-grid-column-span: 3; 
    padding: 0 0 2em 0;
}
.exclusion {
    -ms-grid-row:2; 
    -ms-grid-column:2; 
    height: 160px; 
    width: 200px; 
    padding: 10px;
}

CSS布局模块

要创建 Exclusion,实现浮动图片的文字环绕效果,我只需要为 .exclusion 添加 wrap-flow 属性即可。

.exclusion { 
    -ms-grid-row:2; 
    -ms-grid-column:2; 
    height: 160px; 
    width: 200px; 
    padding: 10px; 
    -ms-wrap-flow:both;
}

属性值 both 意味着文字将会环绕在 Exclusion 的两侧。

CSS布局模块

实现矩形的文字环绕只是 CSS Exclusions 和 Shapes 模块的一小部分特性。在一个早期的浏览器实现中,既可以让文字环绕图形的外部,也可以将文字约束在图形内部。如果你可以使用 Chrome Canary,并且开启了 WebKit 的实验性特性,那么就可以查看由 Adobe 提供的更多示例,甚至是创建自己的实例。这些特性还算是新颖的东西,并具有一定的复杂性,虽然已经在 WebKit 上做了很多测试,依然有可能在最新的浏览器上发生变化。

浏览器提示

wrap-flow 特性对于控制文字环绕效果非常有用,它一旦被大多数的浏览器所支持,一定会快速流行起来。合理构建文档的源码结构,有助于在不支持上述特性的浏览器中优雅降级到另一种布局方案;也有助于在使用类似 Modernizr 的 JavaScript 特性检测方式之后,提供一种可替换的定位模型。

甚至是 Shapes,一旦它被主流浏览器所支持,那么就可以考虑去使用它了。只要文字环绕对于你的设计是一种有益的、非必须的特性,毫无疑问你就可以为那些支持这些特性的浏览器添加它,达到画龙点睛的效果——特别是当你知道访问网站的用户常常会使用这些浏览器的时候。

总结

推敲、把玩和测试这些布局模块是一件激动人心而又妙趣横生的事情。我非常希望本文的内容能够点燃你的热情,让你继续去探索 CSS 中既有和即将到来的新特性,去发现无限的可能性。

在学习这些出色的新特性时,虽然它们可能还不能用于线上实践,虽然会有种种障碍,但对于我们自身的职业发展将会有莫大帮助。参与这些新规范的创建和测试,并不是浏览器开发者和大公司的专利。如果希望未来在浏览器中用到这些新规范,作为网页设计师和开发者的我们,就应该参入到其中。更多信息可以查看推动 Web 发展这个网站:这里提供了多种方式让你参与到 Web 标准的制定中。

##扩展阅读

本文根据@Rachel Andrew的《CSS3 Layout Modules》电子书所整理。如果对此内容感兴趣,可以购买电子书阅读:http://www.fivesimplesteps.com/products/css3-layout-modules

南北

在校学生,本科计算机专业。狂热地想当一名作家,为色彩和图形营造的视觉张力折服,喜欢调教各类JS库,钟爱CSS,希望未来加入一个社交性质的公司,内心极度肯定“情感”在社交中的决定性地位,立志于此改变社交关系的快速迭代。个人博客

如需转载,烦请注明出处:https://www.fedev.cn/css3/css3-layout-modules.htmlView products by sport