Flex 是 Flexible Box 和分配容器中项目之间的空间,即使这些项目的大小未知或是动态。
Flex 布局背后的主要思想是让容器能够改变其项目的宽度/高度(和顺序),以最好地方式填充可用空间(主要是为了适应各种显示设备和屏幕大小)。flex 容器扩展项目以填充可用空间,或收缩项目以防止溢出。
最重要的是,Flex 布局与常规布局(基于垂直的块和基于水平的内联)相比是方向无关的。虽然这些方法对页面很有效,但它们缺乏灵活性来支持大型或复杂的应用程序(尤其是在方向改变、调整大小、拉伸、收缩等方面)。
Flex 布局在 2009 年由 W3C 提出的。它在所有的浏览器都支持:
注意:Flexbox 布局最适合应用程序的组件和小规模布局,而 Grid 布局则适用于大规模布局。
以下内容参考 Flexbox30,图片截自 A Complete Guide to Flexbox
为了使 Flexbox 正常工作,你需要设置父子关系。父级是 Flex 容器,其中的所有内容都是子级或 Flex 项。
Flex 容器仅环绕其直接子容器。因此,没有孙子或孙辈的关系。只有父母 <-> 直系子女!只要存在父子关系,就可以建立 Flexbox。因此,孩子也可以成为其孩子的伸缩容器。但这将是一个单独的 flex 容器。而且它不会继承祖父母的 flex 属性。
还有一点,关于为什么不能将文本容器设置为 flexbox 容器,可以阅读 Never make your text container a flexbox container。
Flexbox 在 2 轴系统中运行:水平的主轴(main axis)和垂直的交叉轴(cross axis)。主轴是你将伸缩项目如何放置在伸缩容器中的定义方向。确定横轴非常简单,它是在垂直于主轴的方向上进行的。
记住不要把他比作数学上的 x 和 y 轴。因为 x 轴并不总是主轴。这可能会让你出错。
在每个轴上都有一个起点和终点。如果在主轴上,则将起始位置称为 main start,将结束位置称为 main end。相同的概念适用于交叉轴。知道起点和终点很重要,因为你可以控制 flex 项目的放置位置。
项目默认沿主轴排列。单个项目占据的主轴空间叫做 main size
,占据的交叉轴空间叫做 cross size
。
父容器和子项目都有自己的一套属性。
父容器:
flex-direction
flex-wrap
flex-flow
justify-content
align-items
align-content
子项目:
flex-grow
flex-shrink
flex-basis
flex
order
align-self
下面,我们会对它们一一讲解。
flex 容器有 2 种类型:flex
将创建一个块级 flex 容器,inline-flex
将创建一个 inline 级 flex 容器。
.parent {
display: flex /* default */ | inline-flex;
}
很简单地解释,块元素占据了容器的整个宽度。它们看起来像构建块,其中每个构建块彼此堆叠。内联元素仅占用其所需的空间。因此,它们似乎排成一行,或者彼此并排。
flex-direction
定义主轴的属性。记住主轴可以是水平或垂直的。因此,如果我们希望主轴是水平的,则称为行。如果我们希望它是垂直的,那就叫做列。另外,请记住我们有一个主要的起点和终点。我们只需添加一个反向后缀即可将 main start 设置为反向。
.parent {
flex-direction: row /* default */ | row-reverse | column | column-reverse;
}
默认情况下,弹性项目将尝试使其自身收缩以适合一行,换句话说,不进行换行。但是,如果你希望 flex 项保持其大小并在容器中的多行中溢出,则可以使用 flex-wrap: warp;
。此属性将使容器中的弹性项目占用多行。
.parent {
flex-wrap: nowrap /* default */ | wrap | wrap-reverse;
}
flex-wrap
允许 flex 项在单独的行上进行包装。但有了 align-content
属性,我们可以控制哪些项目行在横轴上的对齐方式。由于这仅适用于包装的项目,所以如果只有一行 flex 项,则此属性不会有任何效果。
flex-flow
是 flex-direction
和 flex-wrap
的简写。默认值为 row nowrap。如果仅设置一个值,则未设置的属性将采用默认值。
.parent {
flex-flow: row nowrap /* default */ | <flex-direction> <flex-wrap> |
<flex-direction> | <flex-wrap>;
}
justify-content
设置沿主轴对齐的属性。
.parent {
justify-content: flex-start /* default */ | flex-end | center | space-around |
space-between | space-evenly;
}
主轴也可以垂直放置。在这种情况下,将 flex-direction
设置为 column
。
.parent {
flex-direction: column;
justify-content: flex-start /* default */ | flex-end | center | space-around |
space-between | space-evenly;
}
align-items
设置沿横轴对齐的属性。记住横轴始终垂直于主轴。
.parent {
align-items: stretch /* default */ | flex-start | flex-end | center | baseline;
}
现在,让我们看一下如果交叉轴水平放置,则弹性项目如何对齐。换句话说,伸缩方向是列。
.parent {
flex-direction: column;
align-items: stretch /* default */ | flex-start | flex-end | center | baseline;
}
align-content
属性定义了多根轴线的对齐方式。如果项目只有一根轴线,该属性不起作用。
.parent {
align-content: stretch /* default */ | flex-start | flex-end | center |
space-between | space-around;
}
在 space-evenly
中,弹性项目之间的空白空间始终相等。
但是,在 space-around
中,只有内部项目之间的间距相等。第一项和最后一项将仅分配一半的间距。使其具有更加分散的视觉外观:
默认情况下,弹性项目的显示顺序与在代码中显示的顺序相同。但是,如果你要更改该怎么办?没问题!使用 order
属性更改项目的顺序。
.child {
order: 0 /* default */ | <number>;
}
Flexbox 非常适合响应式设计。flex-grow
属性允许我们的 flex 项在必要时增长。因此,如果我的容器中有多余的可用空间,我可以告诉某个特定项目按一定比例将其填满。
.child {
flex-grow: 0 /* default */ | <number>;
}
如果有伸缩空间,flex-grow
将会扩展以填充额外的空间。相反 flex-shrink
当空间不足时将会控制 flex 项目缩小到适合的程度。请注意,数字越大,缩小幅度越大。
.child {
flex-shrink: 1 /* default */ | <number>;
}
你可以在 flex-grow-calculation 和 flex-shrink-calculation 查看浏览器如何帮我们自动处理 flex-grow
和 flex-shrink
,它们是如何计算的出各自该占据多少空间(两者计算方式不同),以及对应的公式。
如果你看完了,觉得自己理解了,可以尝试做一下以下两道题:
使用 flex-basis
属性可以设置项目的初始大小。你可以将此属性视为 flex 项目的宽度。
因此,你的下一个问题可能是 width
和 flex-basis
之间的区别是什么。当然,你仍然可以使用 width
,它将仍然有效。
它起作用的原因是,如果未设置 flex-basis
,它将默认为 width
。因此,你的浏览器将始终尝试查找 flex-basis
值作为大小指示符。如果找不到它,那么它别无选择,只能使用 width
属性。不要让浏览器做额外的工作。使用适当的 flex 方法并使用 flex-basis
。
.child {
flex-basis: auto /* default */ | <width>;
}
当一个项目具有 flex-basis
和 width
时,浏览器将始终使用 flex-basis
设置的值。但要注意,如果同时设置了 min-width
和 max-width
。在这些情况下,flex-basis
将丢失,并且不会用作宽度。
flex
属性是上面所提到的 flex-grow
、flex-shrink
和 flex-basis
的简写形式。如果你足够了解它们的特性,请使用简写吧!
.child {
flex: 1 0 auto /* default */ | <flex-grow> <flex-shrink> <flex-basis> |
<flex-grow> | <flex-basis> | <flex-grow> <flex-basis> | <flex-grow>
<flex-shrink>;
/* 相当于: */
flex-grow: 1;
flex-shrink: 0;
flex-basis: auto;
}
面试经常会问到的一道 CSS 题是弹性盒子中 flex: 0 1 auto
表示什么意思?从上面一路看下来,你应该也知道了它们各自表示什么意思。
align-items
属性可以沿着横轴设置 flex 项。align-items
的问题是它强制所有 flex 项使用规则。
但是如果你想让他们中的一个打破规则,你可以使用 align-self
。此属性接受为 align-items
提供的所有相同值,因此你可以轻松脱离包装 😎
.child-1 {
align-self: stretch /* default */ | flex-start | flex-end | center | baseline;
}
方式一:
.container {
display: flex;
justify-content: center; /* horizontal */
align-items: center; /* vertical */
}
方式二:
.container {
display: flex;
}
.container > div {
margin: auto;
}
对齐 Flexbox 子元素的另一种方法是使用自动外边距。尽管这不是 Flexbox 属性,但要意识到这一点仍然很重要,因为它与 Flexbox 有非常有趣的关系。Bonus: Aligning with Auto Margins
.container > .top {
order: 1;
}
.container > .bottom {
order: 2;
}
.parent {
display: flex;
align-items: center;
padding: 10px;
margin: 30px 0;
}
.child {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.child > h2 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.truncated {
flex: 1;
h2 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
}
创建固定高度的顶栏和动态高度的内容区域。
.container {
display: flex;
flex-direction: column;
}
.container > .top {
flex: 0 0 60px;
}
.container > .content {
flex: 1 0 auto;
}
自动用适当数量的盒子填充可用空间。
.container {
display: flex;
flex-wrap: wrap;
align-content: flex-start;
gap: 1rem;
}
.container > * {
flex: 1 1 10ch;
}
.nav {
display: flex;
flex-flow: row wrap;
justify-content: flex-end;
list-style: none;
margin: 0;
background: deepskyblue;
}
.nav a {
text-decoration: none;
display: block;
padding: 1em;
color: white;
}
.nav a:hover {
background: #1565c0;
}
@media all and (max-width: 800px) {
.nav {
justify-content: space-around;
}
}
@media all and (max-width: 600px) {
.nav {
flex-flow: column wrap;
padding: 0;
}
.nav a {
text-align: center;
padding: 10px;
border-top: 1px solid rgba(255, 255, 255, 0.3);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
.nav li:last-of-type a {
border-bottom: none;
}
}
HTML 结构:
<section>
<header></header>
<main></main>
<footer></footer>
</section>
方式一:
section {
display: flex;
flex-direction: column;
}
main {
flex-grow: 1;
/* flex: 1 0 auto; */
}
footer {
flex-shrink: 0;
}
方式二:
section {
display: flex;
flex-direction: column;
}
footer {
margin-top: auto;
}
推荐:固定页脚
.masonry-container {
display: flex;
flex-direction: column;
flex-wrap: wrap;
max-height: 600px;
.masonry-brick {
margin: 0 0.825rem 0.825rem 0;
border-radius: 1rem;
}
}
/* or */
.masonry-container {
display: flex;
flex-wrap: wrap;
.masonry-brick {
flex: 1 0 auto;
height: 150px;
margin: 0 0.825rem 0.825rem 0;
border-radius: 1rem;
}
}
HTML 结构:
<div class="container">
<header>Lorem</header>
<main>
<aside>Lorem</aside>
<article>Lorem</article>
<nav>Lorem</nav>
</main>
<footer>Lorem</footer>
</div>
CSS 样式:
.container {
display: flex;
flex-direction: column;
}
main {
display: flex;
flex-direction: row;
flex-grow: 1;
}
aside {
width: 25%;
}
article {
flex-grow: 1;
}
nav {
width: 20%;
}
你也可以使用 order
去调整排列顺序。
双飞翼布局其实是根据圣杯布局演化出来的一种布局。
.container {
display: flex;
flex-flow: row nowrap;
justify-content: space-around;
height: 100%;
}
.left,
.right {
width: 200px;
flex-shrink: 1;
}
.main {
flex-grow: 1;
}
.container {
display: flex;
}
.left {
width: 200px;
height: 100%;
}
.right {
flex: 1;
height: 100%;
}