CSS3 弹性盒布局说明(CSS3 Flexible Box Layout Explained)

CSS3 弹性盒布局说明(CSS3 Flexible Box Layout Explained)

文章来源:http://www.smashingmagazine.com/
中文翻译:http://xinyo.org/

翻译正在进行中,预计一周内结束,谬误之处请指正

弹性盒布局,或戏称为“flexbox”,是W3C工作草案(W3C Working Draft)很有趣的一部分。Flexbox规范依旧是个待变更的草案,所以你要时刻关注W3C的变化,等到大部分浏览器都支持它时,它将成为颠覆页面布局的新兴事物。

在此期间,我们可以用flexbox来试验,甚至将它运用在一些旧浏览器也能正确解析的网站上。它成为主流之前都将会是小范围的应用,例如bordr-radius,但是我们的工作是研究新技术,并在可能的地方使用它们。这就是说,当我们遇到页面布局的基本的东西,需要细心对待。

显示属性(The Display Property)

什么是flexbox,它的存在有什么意义?首先,让我们来看看目前页面布局的方式及其遇到的问题。

直到去年,大部分网站还在使用表格table进行页面布局,我怀疑正在读这篇文章的大部分人都曾因依赖表格而犯错。当然,这种布局方式的意义也十分重大,但它只很有局限性。而且,我们还是要面对table语意的含糊和结构的僵化。现在看看以后的主流:CSS 盒模型(CSS box model)。

CSS盒模型允许我们告诉浏览器如何显示一块内容,特别是如何让它作为一个box来显示。在我们仅仅复制粘贴clearfix到CSS文件中之前,会左右摇摆,在读了无数关于clearfix的文章后,我们疲于理解什么是内联块(inline-block)。

基于IE6来测试网站,我们不得不为hasLayout及其后续带来的问题作一些调整:

* html #element {
height: 1%;
}

盒模型能够解决这个问题,并且在多数情况下都运行良好。但是在网络初始的十几年里,这要很多复杂的方式来布局,而且多亏了Mr. Ethan Marcotte — 为浏览器及设备尺寸的解析做出巨大的贡献。

Percentage + Padding + Border = 麻烦

当前有关box模式有另一个问题:padding, marginborder的值都影响box的宽。看看下面的代码:

#element {
width: 50%;
border 1px solid #000;
padding: 0 5px;
}

这将不会得到一个相对于母元素(parent)50%的box,该box 宽度是parent的50%另加12 px(2px border + 10px padding)。你可以把padding设置为百分比(percentage),但是以percentage来设定 border 的宽度时会涉及到数学问题。

解决这个问题有两种方法。第一种是使用CSS3中的box-sizing属性,其值设为boder-box

#element {
box-sizing: border-box;
width: 50%;
border 1px solid #000;
padding: 0 5px;
}

告诉浏览器元素的的宽度与高度包括padding 和 border。(注:content-box 不包括padding和border)

第二种方法是使用 flexbox。

多个问题,多种解决方法

W3C提供了一些列的解决方案:弹性盒模型(flexible box model),列(columns),模板(templates),位置浮动(positioned floats)和grid。

这些显示属性不少于16个值:inline, block, list-item,inline-block, table, inline-table, table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, table-caption, noneinherit.

现在我们可以添加第17个:box

生活在一个盒子里(Living In A Box)

让我们看看flexbox, 它带来了一个新的显示属性值(box)和不少于8个新的属性。下面是W3C对它的定义:

In this new box model, the children of a box are laid out either horizontally or vertically, and unused space can be assigned to a particular child or distributed among the children by assignment of flex to the children that should expand. Nesting of these boxes (horizontal inside vertical, or vertical inside horizontal) can be used to build layouts in two dimensions.

听起来很精彩,W3C工作草案进行了补充:

Flexbox… lacks many of the more complex text or document-formatting properties that can be used in block layout, such as “float” and “columns,” but in return it gains more simple and powerful tools for aligning its contents in ways that Web apps and complex Web pages often need.

弹性盒模型决定了盒模型在那里结束,并且W3C指出:更好的布局模式是为了应对Web应用程序和复杂的网页。下面是新的flexbox属性:

  • box-orient,
  • box-pack,
  • box-align,
  • box-flex,
  • box-flex-group,
  • box-ordinal-group,
  • box-direction,
  • box-lines.

为了简便起见,我们只使用官方的属性和值,但是不要忘了加上前缀。

一切都将改变

如果你花点时间读甚至是浏览最新的工作草案(2011.3.22),你将会注意到很多红色标记的地方,而且都有很好的理由。该规范存在一些问题,并且依旧在变,我们只是在探索。

值得注意的是,本文中使用的语法和当前所有的浏览器,均已过时。在工作草案中,弹性盒模型的语法已经发生了改变。例如:
display: box;
将会变成:
display: flexbox;
其他的变化包括一些属性的拆分,(blox-flex变成flex-growflex-shrink)

,还有一些属性将被合并(box-orientbox-direction变成flex-direction)。事实上,任何以box-开头的属性都将变成以flex-开头。所以要时刻关注规范和浏览器。

PaRappa the Wrapper

使用flexbox经常需要多加一个或两个div ,应为任何一个flexbox元素的母元素都需要 box属性。在此之前,你可以用下面的方式:

<div style="float: left; width: 250px;"> Content here </div>
<div style="float: right; width: 250px;"> Content here </div>

现在用flexbox,将会变成:

<div style="display: box">
  <div style="width: 250px"> Content here </div>
  <div style="width: 250px"> Content here </div>
</div>

或许你已经要放下这篇文章了,觉得这个额外的元素完全是浪费感情。这是可以理解的。但是当你掌握CSS后,就会发现这个小小的div的价值所在。事实上,你将常常已经有了这个包含元素(不一定是div)去添加 display: box

在更广泛的注意,有时你需要表象标志。这只是它的运作方式。我发现,特别是涉及到跨浏览器支持时,我们必须添加表象标记,例如说IE6。我并不是说一定要用“div-itis”,但由于我们都使用HTML5元素的标记,就会发现sections常常需要 div容器。

记住这一点后,让我们来看看代码。我做了一个演示页面,你可以下载源文件

接下来的几段里,我们将会运用flexbox模式来创建一个基本的博客主页。

Box-flex

让我们先从box-flex属性着手。没有box-flex很多功能都没法实现。简而言之,在一个元素相对其父元素(parent)过大或过小时,box-flex能告诉浏览器如何来调整它。

现在我们来分析一个经典的例子。你有三个子元素(children)要放到一个容器(parent)中,而且要让他们依次排列好。换句话说就是让children向左浮动。由于padding,margin和border的存在,children的总宽度可能比parent的宽度大,那么你就得指定宽度或用百分比来控制宽度。

现在假设遇到问题,3个aside宽度为320px(包括padding,margin和border),放在了宽度为920px 的parent中:

现在你能看到内容溢出了,但是如果你给parent添加属性:display: box ,给每个aside添加属性:box-flex: 1 ,然后布局就变成:

那么,到底发生了什么呢?

box-flex 属性告诉浏览器如何来调整box(即parent)的宽度,原理是在box载入后,分配其的未使用空间(包括负值)到每一个aside。其值1是比例因子。

在那个例子中,设每个aside尺寸为320px + 20px 左右的padding,那么就有共1080px,多出容器160px。由于现在每个aside是弹性的,将会相应的缩小,要注意其中padding不变,计算如下:

160 pixels ÷ 3 asides = 53.333 pixels to be taken off each aside.
320 pixels – 53.333 = 266.667 pixels

如果你用Chrmoe的开发工具,将会看到:

就算每个aside 100px,此时也将会被压缩到266.667px来显示。

这个特性十分有用,它会让padding,margin和border保持不变,通过改变元素的尺寸来适应盒的大小,盒的大小如果改变它也会做出适应性的变化。

当然,你也可以给每个元素设置不同的属性值,这样就会有不同的显示比例。现假如有三个依次排列的元素,宽都为100px,padding都为20px,置于宽920px的盒中:

添加box-flex属性:

.box1 { box-flex: 2; }
.box2 { box-flex: 1; }
.box3 { box-flex: 1; }

显示效果如下:

每个aside宽度为140px(100px + 40px padding),共420px,就意味着左边有500px的待填补区域。由比例关系,它们分担空白的比例是2:1:1,这又以下计算:

.box1 = 350px (100px + 250px) + 40px padding
.box2 = 225px (100px + 125px) + 40px padding
.box3 = 225px (100px + 125px) + 40px padding

注意空白的分配只加在元素本身上,padding保持不变。

其实关于数学计算的问题也不大用担心,浏览器会自动帮你搞定。

制作灵活的盒动画

一个简单而雅致的效果已经呈现在你的面前。通过给导航栏li元素添加弹性属性,指定它的悬停(:hover)宽度,就能创造出一个突出该li元素并且压缩其他li元素的效果,css如下:

nav ul {
display: box;
width: 880px;
}

nav ul li {
padding: 2px 5px;
box-flex: 1;
-webkit-transition: width 0.5s ease-out;
min-width: 100px;
}

nav ul li:hover {
width: 200px;
}

效果如下:

除此之外还要给li元素添加 min-width 属性,来应对Chrome的一个Bug。

等高栏:快乐的意外(Equal-Height Columns: The Happy Accident!)

正如我们看到的那样,所有 flexbox 元素都继承了 box-align: stretch 属性,这意味着它们将延伸至充满容器。

例如两个flexbox栏放在有 display:box 属性的盒中,它们将会始终等高。

box-orient 和 box-direction属性

box-orient 属性告诉浏览器块元素在其父元素中的排列方式。默认值是 horizontal(水平),更具体的说是 inline-axis,都是从左到右的排列方式。与此类似,vertical 基本等同于 block-axis ,是块元素从上到下的排列方式,我们将会在水果博客的特色文章中用到。

先看看 box-orient 默认样式:

这样水平排列将会导致溢出,现在看看 box-orient: vertical 的排列方式:

与box-orient相类似的一个属性是box-direction,它决定块元素的展示方向。默认值是normal,对应值是reverse,将会让块左右翻转,正如中国古时从右到左阅读的店铺招牌。

虽然这两个属性是弹性盒模式的重要组成部分,但将不出现在最终规范中,因为它们会被合并到flex-direction property,其拥有以下属性值: lr, rl, tb, bt, inline, inline-reverse, block 和 block-reverse。

1 条评论

  1. phoenixlu2012 年 02 月 28 日下午 4:23 回复

    userful!!