Menu

CSS Flexbox 教程:display flex、轴、对齐与 flex

Flexbox 把项目排成一行或一列,并控制它们如何分配空间、如何对齐。学习 display: flex、主轴和交叉轴、所有容器属性,以及 flex-grow、flex-shrink 和 flex-basis,另附一个能测量每个项目落在哪里的实时演练场。

本页包含可运行的编辑器。编辑、运行,立即查看输出。

display: flex 的作用

在父元素上写 display: flex,它的直接子元素就会排成一行,并排放置,拉伸到相同的高度。要把一排东西布局好,这一条声明就能取代浮动、inline-block 和大部分定位技巧。

在第一个盒子中,每个 div 都占满一整行。在第二个盒子中,三个项目排在同一行,每个只和自己的内容一样宽,而 One 和 Three 被拉伸到和 Two 一样高。只改了父元素:display: flex 要写在容器上,永远不写在项目上。

Flexbox 演练场

选择不同的值,看看项目怎样移动。盒子下面那行代码就是你所看到效果的 CSS,再下面的列表是浏览器实际放置每个项目的位置,用 getBoundingClientRect() 测量得出。

可以试试这些:

  • justify-content: space-between:项目 1 落在 x 0 处,项目 3 贴着另一侧的边缘。
  • align-items: center:三个项目保持各自的高度,中心对齐在一条线上。在默认的 stretch 下,项目 1 没有自己的高度,会填满容器的全部 170px。
  • flex-direction: column:现在 justify-content 让项目在垂直方向移动,align-items 在水平方向移动。这两个属性跟随的是轴,而不是屏幕。

主轴和交叉轴

每个 flex 容器都有两个轴。主轴是项目排列的方向,由 flex-direction 设置。交叉轴与主轴垂直。

flex-direction主轴交叉轴justify-content 移动项目的方向align-items 移动项目的方向
row(默认值)水平,按阅读顺序垂直水平垂直
row-reverse水平,方向相反垂直水平垂直
column垂直,从上到下水平垂直水平
column-reverse垂直,从下到上水平垂直水平

在从右到左的页面中(dir="rtl"),row 从右边缘开始,因为主轴跟随书写方向。

容器属性

这些属性写在带 display: flex 的元素上:

属性作用默认值
flex-direction行还是列,以及朝哪个方向row
flex-wrap空间不够时项目是否换到新的一行nowrap
flex-flowflex-direction 和 flex-wrap 的简写row nowrap
justify-content沿主轴的位置和间距normal(表现为 flex-start)
align-items项目沿交叉轴的位置normal(表现为 stretch)
align-content项目换行时各行之间的间距normal
gap项目之间的间距(row-gap、column-gap)0

justify-content 和 align-items 各有一个讲解所有取值的页面:justify-content 和 align-items。换行和方向见 flex-wrap 页面。

项目属性:flex-grow、flex-shrink、flex-basis

这些属性写在子元素上,决定它们如何分配容器的宽度:

  • flex-basis:分配剩余空间之前,项目的起始尺寸(auto 表示“用我的宽度或内容的宽度”)。
  • flex-grow:项目占据剩余空间的份数。0 表示它永远不会增长。
  • flex-shrink:空间不够时项目让出多少。0 表示它永远不会收缩。

flex 简写一次设置全部三个属性:

写法含义表现
flex: initial(默认值)0 1 auto内容尺寸,可以收缩,永远不增长
flex: 11 1 0%从零开始,平分空间
flex: auto1 1 auto内容尺寸再加上一份剩余空间
flex: none0 0 auto正好是内容或宽度的尺寸,既不增长也不收缩
flex: 22 1 0%份额是 flex: 1 项目的两倍

每一行在内边距内宽 480px,两个 8px 的间距之后,留给项目的是 464px。打印出的宽度显示了空间是如何分配的:第一行每个约 155px;第二行是 116 + 232 + 116(flex: 2 的项目得到的份额是两边项目的两倍);第三行是 100 + 372,flex: none 的项目保持自己的宽度,另一个项目拿走其余所有空间。

用 flexbox 实现真实布局

开头放 logo、末尾放链接的页头是最常见的 flex 布局。在某个项目上写 margin-inline-start: auto,它会吃掉自己前面所有的剩余空间,把自己和后面的所有内容推到末尾:

align-items: center 让较大的 logo 文字和链接对齐,gap 设置链接之间的间距,不需要再为最后一个链接去掉外边距。

另一种日常用法是媒体行:一个固定尺寸的图标,加上占据其余空间并在自己的列中换行的文字。

flex: none 是关键的保证。flex 项目默认会收缩,所以如果去掉圆形上的它,同时去掉文字上的 flex: 1,段落就会把圆形挤到约 23px 宽:变成椭圆。要让一个东西在盒子里居中,flexbox 只需要两行(justify-content: center; align-items: center);div 居中页面把它和其他方法做了对比。

order 与无障碍

order 属性和 -reverse 方向改变的是项目显示的位置,而不是它们在 HTML 中的位置。屏幕阅读器和 Tab 键仍然按照 HTML 中的顺序,所以键盘用户可能会从屏幕上的第一个链接直接跳到最后一个。它们只用于装饰;当顺序有实际含义时,请修改 HTML。

flexbox 还是 grid

Flexbox 一次只在一个方向上工作:一行中的项目根据内容决定尺寸,换行后的第二行不会和第一行对齐。Grid 在两个方向上工作:你定义好列和行,项目就会对齐到网格中。导航栏、工具栏或表单行用 flexbox;图片墙或带侧边栏的页面用 grid。

常见错误

  • 把 display: flex 写在项目上,而不是父元素上。 容器设置 display: flex,它的子元素才是项目。
  • 没有高度却期望 align-items 让项目垂直移动。 如果容器只和最高的项目一样高,就没有可以移动的空间。给容器设置高度;否则它其实在正常工作,只是没什么可做的。
  • 文字溢出而不是收缩。 flex 项目带有 min-width: auto。给需要收缩的项目加上 min-width: 0。
  • 用了 flex: 1,却奇怪宽度为什么不一样。 带 flex: 1 的项目会平分空间,但内容放不下的项目(一个很长的 URL、一张图片)不会收缩到比内容更窄。min-width: 0 同样能解决这个问题。
  • 在项目之间用外边距。 在容器上用 gap;它只作用于项目之间。
  • 不必要的嵌套 flex。 只有直接子元素才是 flex 项目。孙元素遵循正常布局,除非它们自己的父元素也是 display: flex。

常见问题

display: flex 有什么作用?

它把一个元素变成 flex 容器。它的直接子元素成为 flex 项目,默认排成一行,并排放置,拉伸到相同的高度,再由 justify-content、align-items 和 gap 等属性控制它们如何分配空间。

flex: 1 是什么意思?

flex: 1 是 flex: 1 1 0% 的简写:项目可以增长(flex-grow: 1),可以收缩(flex-shrink: 1),并且从零尺寸开始(flex-basis: 0%)。都带 flex: 1 的项目会平分剩余空间,所以它们最终一样宽,除非内容放不下。

justify-content 和 align-items 有什么区别?

justify-content 沿主轴放置项目(在一行中就是水平方向),align-items 沿交叉轴放置项目(在一行中就是垂直方向)。设置 flex-direction: column 后两个轴互换,justify-content 就变成了垂直方向。

应该用 flexbox 还是 grid?

如果是单独的一行或一列,并且项目的尺寸由内容决定,就用 flexbox:导航栏、按钮组、输入框旁边的标签。如果你需要行和列彼此对齐,例如卡片网格或页面布局,就用 grid。

为什么我的 flex 项目会溢出容器?

flex 项目默认有 min-width: auto,所以它不肯收缩到比最长的单词或最宽的子元素还窄。在项目上设置 min-width: 0(或 overflow: hidden)就能让它收缩,例如让 text-overflow: ellipsis 生效。

flexbox 对 span 这样的行内元素有效吗?

有效。任何带 display: flex 的元素都是 flex 容器,无论子元素自己的 display 值是什么,都会成为 flex 项目,所以 <span> 子元素不再是行内元素,而是像块元素一样接受宽度、高度和外边距。

Coddy 编程语言插图

和 Coddy 一起学编程

开始