使用 CSS 弹性盒子

CSS3 弹性盒子(Flexible BoxFlexbox),是一种用于在页面上布置元素的布局模式使得当页面布局必须适应不同的屏幕尺寸和不同的显示设备时,元素可预测地运行。对于许多应用程序,弹性盒子模型提供了对块模型的改进,因为它不使用浮动,flex容器的边缘也不会与其内容的边缘折叠。

许多设计师会发现弹性盒子模型更易于使用。弹性盒子中的子元素可以在各个方向上进行布局,并且能以弹性尺寸来适应显示空间。由于元素的显示顺序可以与它们在源代码中的顺序无关,定位子元素将变得更容易,并且能够用更简单清晰的代码来完成复杂的布局。这种无关性是仅限制于视觉呈现上的,语言顺序以及基于源代码顺序的导航均不受影响。

注意: 虽然 CSS 弹性盒子布局规范 还处于最终征求意见稿 (Last Call Working Draft)阶段(参见最新编辑草案),并非所有浏览器都实现了弹性盒子的所有功能。但,这么说吧,现在全线产品对弹性盒子都有良好支持。最新的兼容性状况可以查看每个具体属性的兼容性表格获取。

弹性盒布局概念

在定义方面来说,弹性布局是指通过调整其内元素的宽高,从而在任何显示设备上实现对可用显示空间最佳填充的能力。弹性容器扩展其内元素来填充可用空间,或将其收缩来避免溢出。

块级布局更侧重于垂直方向、行内布局更侧重于水平方向,与此相对的,弹性盒子布局算法是方向无关的。虽然块级布局对于单独一个页面来说是行之有效的,但其仍缺乏足够的定义来支持那些必须随用户代理(user agent)不同或设备方向从水平转为垂直等各种变化而变换方向、调整大小、拉伸、收缩的应用程序组件。 弹性盒子布局主要适用于应用程序的组件及小规模的布局,而(新兴的)栅格布局则针对大规模的布局。这二者都是 CSS 工作组为在不同用户代理、不同书写模式和其他灵活性要求下的网页应用程序有更好的互操作性而做出的更广泛的努力的一部分。

弹性盒布局相关词汇

关于弹性盒子的讨论已经从诸如水平/行内轴和垂直/块级轴这些术语中解放出来,与此同时,需要有一套新的术语来正确描述此模型。在学习下面的词汇项目时请对照下图。图中是一个 flex-direction 属性为 row的弹性容器,意味着其内的弹性项目将根据既定书写模式沿主轴水平排列,其方向为元素的文本流方向,在这个例子里,为从左到右。

弹性布局相关名词

弹性容器(Flex container)
包含着弹性项目的父元素。通过设置 display 属性的值为 flexinline-flex 来定义弹性容器。
弹性项目(Flex item)

弹性容器的每个子元素都称为弹性项目。弹性容器直接包含的文本将被包覆成匿名弹性单元。

轴(Axis)

每个弹性框布局包含两个轴。弹性项目沿其依次排列的那根轴称为主轴(main axis)。垂直于主轴的那根轴称为侧轴(cross axis)

  • flex-direction 确立主轴。
  • justify-content 定义了在当前行上,弹性项目沿主轴如何排布。
  • align-items 定义了在当前行上,弹性项目沿侧轴默认如何排布。
  • align-self 定义了单个弹性项目在侧轴上应当如何对齐,这个定义会覆盖由 align-items 所确立的默认值。
方向(Direction)

弹性容器的主轴起点(main start)/主轴终点(main end)侧轴起点(cross start)/侧轴终点(cross end)描述了弹性项目排布的起点与终点。它们具体取决于弹性容器的主轴与侧轴中,由 writing-mode 确立的方向(从左到右、从右到左,等等)。

  • order 属性将元素与序号关联起来,以此决定哪些元素先出现。
  • flex-flow 属性是 flex-directionflex-wrap 属性的简写,决定弹性项目如何排布。
行(Line)

根据 flex-wrap 属性,弹性项目可以排布在单个行或者多个行中。此属性控制侧轴的方向和新行排列的方向。

尺寸(Dimension)

根据弹性容器的主轴与侧轴,弹性项目的宽和高中,对应主轴的称为主轴尺寸(main size) ,对应侧轴的称为 侧轴尺寸(cross size)

定义一个弹性盒子

为要使用此样式的元素指派 CSS,需按以下方式设置 display 属性:

display : flex

或者

display : inline-flex

这样做将元素定义为弹性容器,其子元素则成为弹性项目。值 flex 使弹性容器成为块级元素。值 inline-flex 使弹性容器成为单个不可分的行内级元素。

注意:厂商前缀标记会附加给 display 属性值,而不是加给 display 属性本身。例如:display : -webkit-flex

弹性项目须知

弹性容器直接包含的文本将自动包覆成匿名弹性项目。不过,一个只包含一系列空白符(如一堆空格或制表符等)的匿名弹性项目不会被渲染,就如同对其指派 display: none

对于弹性容器的绝对定位子元素来说,其静态位置参照弹性容器的内容框的主起始角确定,而后依此完成此元素的定位。

相邻的弹性元素其外边距不会互相合并。使用 auto 外边距可以吸收掉水平或垂直方向上的额外空间,这可以用于对齐或分隔相邻的弹性项目。更多细节请参考 W3C 弹性框布局模型规范中的 Aligning with 'auto' margins

不像 CSS 中的其他对齐方法,弹性框的对齐属性将进行“真正的”居中对齐。这意味着即使弹性条目溢出了弹性容器,它依然保持居中。不过这在某些时候可能会有问题。如果溢出超过了页面的上边缘或左边缘(在从左到右的语言中,比如英语;在诸如阿拉伯语这样从右到左的语言中这个问题更会出现在右边缘),则虽然那些地方确实有内容,却无法滚动到那些位置。在未来的发布版本里,对齐属性将会有所扩展,使其包含有“安全”选项。目前,如果操心这点,可以改用外边距来达成居中效果,因为外边距会用比较“安全”的方式来响应变化,出现溢出时将停止居中。对这种需要居中的弹性项目,不使用 align- 属性,而使用自动外边距就能解决这个问题。对弹性容器中第一个和最后一个弹性项目的外侧边缘应用,也可以使用自动外边距来替代 justify- 属性。自动外边距会自动伸缩来占满剩余空间,当有剩余空间存在时弹性项目将会居中,如果没有则切换至常规对齐方式。不过很不幸,如果尝试在多行的弹性框中用基于外边距的居中方法来替代 justify-content,就必须对每一行的第一个和最后一个弹性项目应用外边距。此时除非能够事先预测每一行都结束于哪个元素,否则就不能愉快的在主轴方向上用基于外边距的居中方法来替代 justify-content 属性了。

再强调一遍,元素的显示顺序与它们在源代码中的顺序无关,这种无关性只影响视觉呈现,语音顺序以及基于源代码顺序的导航均不受影响。order 属性并不影响语音和导航的次序。因此开发者们必须小心,合理地安排元素在源代码中的顺序,以免破坏文档的可访问性。

弹性盒子相关属性

不影响弹性盒子的属性

由于弹性盒子使用了不同的布局算法,某些属性用在弹性容器上没有意义:

  • 多栏布局模块column-* 属性对弹性项目无效。
  • floatclear 对弹性项目无效。使用 float 将使元素的 display 属性计为block
  • vertical-align 对弹性项目的对齐无效。

示例

基本的弹性布局示例

这个基本的示例展示了如何对元素应用弹性布局,以及在弹性布局状态下相邻元素的行为方式。

<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
   .flex
   {
      /* 基本样式 */
      width: 350px;
      height: 200px;
      border: 1px solid #555;
      font: 14px Arial;
      /*  建立弹性框 */
      display: -webkit-flex;
      -webkit-flex-direction: row;
      display: flex;
      flex-direction: row;
   }
   .flex > div
   {
      -webkit-flex: 1 1 auto;
      flex: 1 1 auto;
      width: 30px; /* 让过渡表现良好。(从/到"width:auto"的过渡
                      至少在 Gecko 和 Webkit 上是有 bug 的。
                      更多信息参见 http://bugzil.la/731886 ) */
      -webkit-transition: width 0.7s ease-out;
      transition: width 0.7s ease-out;
   }
   /* colors */
   .flex > div:nth-child(1){ background : #009246; }
   .flex > div:nth-child(2){ background : #F1F2F1; }
   .flex > div:nth-child(3){ background : #CE2B37; }
   .flex > div:hover
   {
        width: 200px;
   }
   </style>
 </head>
 <body>
  <p>Flexbox nuovo</p>
  <div class="flex">
    <div>uno</div>
    <div>due</div>
    <div>tre</div>
  </div>
 </body>
</html>

圣杯布局示例

此示例展示了弹性盒子根据不同屏幕分辨率动态改变布局的能力。下图说明了这种转换。

HolyGrailLayout.png

这里展示的正是针对浏览器窗口的页面布局必须为智能手机窗口优化的场景。不仅元素的尺寸需要缩减,其呈现顺序也需要改变。弹性盒子让这变得很简单。

<!DOCTYPE html>
<html lang="en">
  <head>
    <style>
  body {
   font: 24px Helvetica;
   background: #999999;
  }
  #main {
   min-height: 800px;
   margin: 0px;
   padding: 0px;
   display: -webkit-flex;
   display:         flex;
   -webkit-flex-flow: row;
           flex-flow: row;
   }
  #main > article {
   margin: 4px;
   padding: 5px;
   border: 1px solid #cccc33;
   border-radius: 7pt;
   background: #dddd88;
   -webkit-flex: 3 1 60%;
           flex: 3 1 60%;
   -webkit-order: 2;
           order: 2;
   }
  #main > nav {
   margin: 4px;
   padding: 5px;
   border: 1px solid #8888bb;
   border-radius: 7pt;
   background: #ccccff;
   -webkit-flex: 1 6 20%;
           flex: 1 6 20%;
   -webkit-order: 1;
           order: 1;
   }
  #main > aside {
   margin: 4px;
   padding: 5px;
   border: 1px solid #8888bb;
   border-radius: 7pt;
   background: #ccccff;
   -webkit-flex: 1 6 20%;
           flex: 1 6 20%;
   -webkit-order: 3;
           order: 3;
   }
  header, footer {
   display: block;
   margin: 4px;
   padding: 5px;
   min-height: 100px;
   border: 1px solid #eebb55;
   border-radius: 7pt;
   background: #ffeebb;
   }
  /* 窄到已不足以支持三栏 */
  @media all and (max-width: 640px) {
   #main, #page {
    -webkit-flex-flow: column;
            flex-direction: column;
   }
   #main > article, #main > nav, #main > aside {
    /* 恢复到文档内的自然顺序 */
    -webkit-order: 0;
            order: 0;
   }
   #main > nav, #main > aside, header, footer {
    min-height: 50px;
    max-height: 50px;
   }
  }
 </style>
  </head>
  <body>
 <header>header</header>
 <div id='main'>
    <article>article</article>
    <nav>nav</nav>
    <aside>aside</aside>
 </div>
 <footer>footer</footer>
  </body>
</html>

试验场地

有几个在线弹性盒子试验场地可供进行各种实验:

务必牢记

描述弹性项目如何排布的算法有时会极其棘手。在使用弹性盒子进行设计时,请考虑以下几点,以免碰到不好的意料外状况。

弹性盒子的排布与书写模式是一致的,这意味着排布的主轴起点主轴终点根据的是开始结束的位置。

侧轴起点侧轴终点依赖于开始前面(before)的位置定义,而这个“前面”依赖于 direction 的值。

只要 break- 属性的设置值允许,在弹性框布局中是可以存在分页的。CSS3 中的 break-afterbreak-beforebreak-inside,以及 CSS 2.1 中的 page-break-beforepage-break-afterpage-break-inside 属性在弹性容器上、弹性项目上和弹性项目内均可以使用。

浏览器兼容性

特性 Firefox (Gecko) Chrome Internet Explorer Opera Safari
基础支持(单行弹性框) 18.0 (18.0)[6]-moz[2]
22.0 (22.0)
21.0-webkit
29.0
11[3] 12.10-webkit[5] 6.1-webkit[1]
多行弹性框 28.0 (28.0) 21.0-webkit
29.0
11[3] 12.10[5]
15 -webkit
6.1-webkit[1]
特性 Firefox Mobile (Gecko) Firefox OS Android IE Phone Opera Mobile Safari Mobile
基础支持(单行弹性框) 18.0 (18.0)-moz[2]
22.0 (22.0)

1.0-moz[2]
1.1

2.1-webkit[4]
4.4
11 12.10[5]
15-webkit
7-webkit[1]
多行弹性框 28.0 (28.0) 1.3 2.1-webkit[4]
4.4
11 12.10[5]
15-webkit
7-webkit[1]

[1] Safari 在版本 6.0 (iOS.1)以前支持的是规范的一个与现有版本不兼容的旧版草案。Safari 6.1(以及 iOS 7 上的 Safari)已更新为支持最终版本。

[2] 直到 Firefox 22 位置,用户必须修改 about:config 设置,将 layout.css.flexbox.enabled 改为 true 才能激活对弹性盒子的支持。从 Firefox 22 到 Firefox 27,此设置项默认为 true,而 Firefox 28 中取消了此设置项。

[3] Internet Explorer 10 支持的是规范的一个与现有版本不兼容的旧版草案;Internet Explorer 11 已更新为 支持最终版本。

[4] Android 浏览器直到 4.3 为止支持的是规范的一个与现有版本不兼容的旧版草案。Android 4.4 已更新为支持最终版本。

[5] 在 Opera 12.10 的弹性盒子初始实现中是没有前缀的,但 Opera 版本 15 到 16 和 Opera Mobile 的 15 到 19 需要 -webkit. 在 Opera 17 及 Opera Mobile 24 中再次取消了前缀。

[6] 直到 Firefox 29 为止,在弹性项目上指定 visibility: collapse 将使其被视为 display: none 处理,而预期的行为是被视为 visibility: hidden。建议的处理方式是在弹性项目上使用 visibility:hidden,这样其行为应当与指派了 visibility:collapse 一致。更多信息,参考 bug 783470.

参见

文档标签和贡献者