弹性盒布局(Flexible Box Layout),通常被大家叫做Flexbox,是一种在CSS中非常实用且功能强大的布局方式。它完全改变了我们设计网页布局的方法,让那些以前很难实现的布局效果变得轻而易举。使用Flexbox,我们通常不需要依赖复杂的CSS框架,只用几行简单的代码就能创建出网站需要的各种布局样式。

Flexbox的设计理念其实非常直白:它通过合理分配空间、对齐内容以及调整元素的显示顺序来安排页面中的各个组件。我们可以轻松地把内容按照水平或垂直方向排列,可以沿着一条轴线来布局,也可以让内容自动换行到多行显示。Flexbox的能力其实还有很多我们没提到的地方。
Flexbox的运行机制基于一种清晰的父子关系。当我们给某个元素添加display: flex或者display: inline-flex属性时,这个元素就变成了一个弹性容器(flex container),它会负责管理内部子元素的排列和布局。而容器的直接子元素就自动成为了弹性项目(flex items)。
这种父子关系有个关键特点:弹性容器只会直接影响它的直系子元素,不会影响到更深层次的孙子元素或其他后代。不过,我们可以让那些孙子元素也变成弹性容器,这样就能构建出非常复杂的多层嵌套布局结构。
当我们使用display: flex时,容器会成为块级元素,在页面中单独占一行,内部采用弹性布局。而使用display: inline-flex时,容器会成为行内块级元素,可以与其他内容在同一行显示,内部同样使用弹性布局。两者主要区别在于容器自身在文档流中的表现形式。
在弹性容器里面,各个项目是沿着主轴(main-axis)来进行排列的。主轴既可以是水平的,也可以是垂直的,这样我们就能把项目排成一行或者一列。主轴的具体方向是由当前的文字书写模式决定的。
当我们创建了一个弹性容器后,如果里面的项目没有占满整个主轴空间(比如容器的宽度),就会剩下一些多余的空间。有些属性可以控制这些多余空间的处理方式,我们可以让子元素靠左聚集、靠右聚集或者居中,也可以让它们分散开来,规定空间应该如何在子元素之间或者周围进行分配。
Flexbox最厉害的功能之一,就是它能让元素在各种屏幕尺寸和设备上保持一致的表现。Flexbox特别适合做响应式网站,因为当可用空间变大或变小时,内容可以相应地放大或缩小。这让我们能够轻松创建出适应各种设备和屏幕尺寸的布局,而不需要写很多复杂的媒体查询。
让我们通过一个简单的例子来理解Flexbox的基本用法。假设我们要用一组链接创建一个导航栏,这正是Flexbox设计的典型应用场景。
|<nav> <a href="/">首页</a> <a href="/about">关于</a> <a href="/blog">博客</a> <a href="/contact">联系</a> </nav>
在上面的例子中,给<nav>元素添加display: flex属性后,这个<nav>标签就变成了“弹性容器”(flex container),它内部的链接元素(<a>)也随之成为“弹性项目”(flex item)。这些链接本质上还是超链接标签,但在弹性布局的上下文下,具备了弹性项目的特性,也就是说它们不再遵循传统的行内元素行为,而是受Flexbox弹性模型的规范管理。
需要特别注意的是,Flexbox 有一个非常实用的特点:它会自动过滤掉HTML标签(比如多个<a>标签)之间的空白字符。对有经验的前端来说,以前常常要想办法去除HTML元素间的空格(比如通过注释方式)才能实现精确的布局,而有了Flexbox,这个问题得到了解决,使布局更为简洁直观。
Flexbox布局系统最大的优点之一就是与方向无关性。这与早期的块级布局(block)和行内布局(inline)有显著区别。传统的布局模式在大多数情况下只考虑垂直和水平方向;例如,老式网页主要针对桌面显示器,假定水平方向受限而垂直可以无限延伸。但在当今多样化设备和不断变化的显示需求下,页面布局往往需要在不同的主轴方向上自由伸缩,甚至还需要适应文字书写方向的变化。
许多经典的排版挑战,例如多元素垂直居中或多栏等宽、按钮底部对齐等问题,在Flexbox普及前都非常棘手。而现在,通过弹性盒模型,这些问题都可以用简单的CSS代码轻松实现。
理解Flexbox的第一步,就是掌握弹性容器这一基本概念。当给某个容器元素指定display: flex或display: inline-flex后,它就成为了弹性盒布局的容器,为内部的所有直接子节点提供弹性布局环境。
在这种情况下,容器内的每个直接子节点(包括普通DOM元素、文本节点、伪元素等)都会被视为弹性项目(flex item)。即使是带有绝对定位的子元素,也会以弹性项目的方式受到部分Flexbox规则影响,不过它们的尺寸和定位会有特殊处理(视为容器内唯一弹性项目)。
无论你想让内容横向排布、纵向排列,还是实现逆向(右到左/下到上)排序,都可以通过flex-direction属性来设定主轴的方向,对应地控制所有弹性项目在主轴上的排列顺序。
flex-direction属性有四个可选值:row(水平排列,这是默认值)、row-reverse(水平反向排列)、column(垂直排列)和column-reverse(垂直反向排列)。这些值决定了弹性项目在容器中的排列方向。
假设我们有一个简单的有序列表结构:
|<ol> <li>项目一</li> <li>项目二</li> <li>项目三</li> <li>项目四</li> <li>项目五</li> </ol>
|/* 默认值 row - 从左到右排列 */ ol {
默认的row值看起来和一堆行内元素或浮动元素差不多,但这只是表面现象。注意其他flex-direction值是如何影响列表项排列的。比如,我们可以用flex-direction: row-reverse来反转排列方向。当设置flex-direction: column时,弹性项目会从上到下垂直排列;如果设置为flex-direction: column-reverse,项目就会从下到上反向排列。
切勿通过修改flex-direction属性来实现页面的语言方向切换(比如右到左的语言排布)。正确做法应当借助HTML的dir属性或CSS的writing-mode属性,显式设定文本和版面的主流方向。
当设置flex-direction: column时,Flex容器的主轴会根据当前文档的书写模式与块轴对齐。例如,在英语等采用水平书写的场景下,主轴实际上是从上到下的垂直方向;而在如传统日语这种竖排书写方式下,主轴则会变为横向。
因此,如果你在英文等横排语言环境下将flex容器方向改为column,各个子项会按照HTML源码的顺序依次自上而下排列,排成一列纵向分布,不再是横向并排显示。
我们完全可以通过几行CSS,将原本水平排列的导航列表,优雅地“转身”为竖直的侧边栏布局。这一步只需把flex-direction从默认的row换为column,与此同时可将分隔线由底边移动到右侧,视觉效果就发生了明显变化。
至于flex-direction: column-reverse,它与column类似,只是主轴方向相反:此时主轴的起点main-start切换到末尾,终点main-end则变为开始位置。举例来说,在横排文档中,flex项目将会从下向上逆序排列。需要注意的是,虽然视觉顺序发生变化,HTML中的Tab焦点和阅读顺序仍然保持不变。
在设计多语言页面时(如既有LTR语言如英文,也有RTL语言如阿拉伯文),Flexbox结合合适的书写属性,能够自动适应不同的起始方向。例如,对于LTR(如英语)和RTL(如阿拉伯语)的网页,默认的flex-direction: row均能按预期完成主轴方向控制。
实际上,flex-direction: row会结合文档的书写方向(受dir或direction、writing-mode等影响)来排列内容,所以不论内容是从左到右还是从右到左,Flexbox都能处理得当。当切换书写模式(比如从水平到垂直),Flexbox也会智能调整弹性主轴的指向,无需手动干预。
设置书写方向的方法很多,比如在HTML标签(或任意元素)上加dir属性,或者在CSS中设置writing-mode、direction以及text-orientation。其中,当dir被指定为rtl时,主轴在默认row下自动变为右到左排列,完全兼容多语言布局需求。
如果你同时为某个元素指定了HTML的dir属性和CSS的direction样式,最终会以CSS的direction值为准。官方规范更推荐优先采用HTML属性以提升代码可维护性。
拥有垂直书写习惯的语言有许多,比如中文注音符号、埃及象形文字、日语假名、汉字、韩文及少数民族文等。这类语言只有在明确设置了竖排(如writing-mode: vertical-rl)时才会竖向显示,否则在网页中依然以横排方式展现。
以从上到下书写(vertical writing)的页面为例,writing-mode: horizontal-tb时,Flexbox的主轴会相对于默认左到右方向逆时针旋转90度。例如,flex-direction: row导致项目从上到下纵向排列,而flex-direction: column则会让内容从右向左水平排列,这意味着主轴、交叉轴的实际逻辑都会随之发生变化。
默认情形下,弹性容器的所有项目会强制排在同一“主轴”上,即使内容太多装不下,元素也不会自动换行或自适应收缩。如果设置的flex属性允许,子项可能缩小以适应容器,否则就会溢出显示。
为了让弹性项目可以智能地拆分成多行或多列,可以利用flex-wrap属性来控制。它决定了弹性盒是必须保持单行,还是可以根据需要分段分行。
flex-wrap支持三种取值:nowrap(不换行,默认)、wrap(主轴溢出时自动换行)、wrap-reverse(反方向自动换行)。配置不同带来的换行效果请结合实际布局测试。
当你启用flex-wrap: wrap或wrap-reverse,若主轴内容超限,弹性容器会视配置决定新的项目行应该“追加”在容器主轴的哪个端点。具体而言:
wrap时,新增项目行会沿着交叉轴的“主流方向”出现。例如,row和row-reverse时,后续行会出现在已有行的下方;column和column-reverse时,后续列则出现在右侧。wrap-reverse后,交叉轴方向反转。例如,使用row或row-reverse,溢出的弹性行会出现在上一行的上方;而在column-reverse情况下(比如英语这种LTR环境),新列会出现在最左侧。通过合理配合flex-direction与flex-wrap,你可以实现极其灵活和多样的布局效果,适应几乎所有主流的书写模式和页面排版需求。
|<div class="container"> <div class="item">项目1</div> <div class="item">项目2</div> <div class="item">项目3</div> <div class="item">项目4</div> <div class
flex-flow属性是Flexbox布局中用来同时控制主轴方向以及是否换行的复合属性。它实际上是flex-direction和flex-wrap这两个属性的合体,可以简洁地同时指定弹性容器的主轴方向及其子元素是否允许换行。
当我们将一个元素的display设为flex或inline-flex时,如果没有特别设置flex-flow、flex-direction或flex-wrap,效果默认等同于以下三种写法的任意一种:
|flex-flow: row; flex-flow: nowrap; flex-flow: row nowrap;
这意味着在从左到右(LTR)书写模式下,如果省略了这些属性,弹性容器的主轴会是水平方向,并且其子项不会自动换行。
如果需要让弹性项目以反向的列排列且能换行,有两种等效写法可以选择:
|flex-flow: column-reverse wrap; flex-flow: wrap column-reverse;
这些设置在英文等LTR语言环境下,会使项目由下向上堆叠,并以纵向新列的方式换行。而在日语等竖排书写模式中,布局则会调整为从右到左横向堆叠,并从上到下进行换行。具体流向依据当前的书写模式与方向性而定。
在学习Flexbox弹性布局时,常会遇到“主轴”(main-axis)和“交叉轴”(cross-axis)这两个概念。它们分别代表弹性项目排列的主要方向和换行或分布的垂直方向,是理解弹性布局的基础。
简而言之,所有的弹性项目会沿着所谓的主轴(main-axis)排列。例如,如果设置了flex-direction: row,主轴就是水平方向。如果采用flex-direction: column,主轴则变为竖直方向。而每一行弹性项目之间的分布则是沿着交叉轴(cross-axis)展开。
在仅用一行弹性项目,或者不启用换行(flex-wrap: nowrap)的情景下,所有元素都按主轴从main-start流动到main-end排列。如果允许换行(比如flex-wrap: wrap),那么多余的项目就会在交叉轴方向上自动另起一行(或一列),新行(或列)按照cross-start到cross-end在交叉轴上分布。
这些专业名词,如main-start、main-end、cross-start和cross-end,有时候看起来像CSS中的逻辑属性(例如margin-inline-start),但它们的具体含义完全由flex-direction、flex-wrap配置和书写模式(如LTR、RTL或垂直排布)共同决定,因此理解其对应关系在实践中尤为关键。
在弹性盒布局(Flexbox)中,“主轴”是弹性容器内项目排列的参考方向。简单来说,主轴表示布局中内容流动的主要路径。例如,如果主轴是水平方向,所有的弹性项目会按照水平方向一字排开。主轴的长度在Flexbox中被称作主轴尺寸(main-size),而主轴的两端分别对应主轴起点(main-start)和主轴终点(main-end),所有项目的排列顺序就从main-start指向main-end逐步推进。
与主轴垂直的方向被称作交叉轴(cross axis),它用于确定弹性行在容器内叠加的方向。可以把交叉轴理解为每当一行弹性项目满员后,下一行插入的位置和方向。容器在交叉轴方向的空间总量被称为交叉轴尺寸(cross-size),而交叉轴的起始和结束分别是交叉轴起点(cross-start)和交叉轴终点(cross-end)。当允许弹性项目换行时,每新一行的加入就会按照交叉轴的方向逐层叠加。
需要特别注意,文档的书写方向(如从左到右LTR或从右到左RTL)会直接影响主轴和交叉轴的对应方向。本教程以LTR(左到右)为基础进行讲解,并在相关位置补充不同书写模式下的表现差异。
通过设置flex-direction属性,我们可以灵活指定弹性项目在主轴上的排列方向(如水平还是垂直),这些项目的排序始于main-start并指向main-end。如果此时开启了flex-wrap允许项目自动换行,新的弹性行就会从cross-start方向依次堆叠到cross-end。
举例来说,当采用水平方向的主轴(即row和row-reverse)时,交叉轴通常是垂直方向。此时,如果设置了flex-flow: row wrap或flex-flow: row-reverse wrap,每增加一行都从上到下依次增加。在这种模式下,主轴尺寸指的是容器的宽度,交叉轴尺寸则对应高度;对于column和column-reverse来说,主轴为垂直方向,此时主轴尺寸是高度,交叉轴尺寸则变为宽度。无论是LTR还是RTL书写方式,都遵循上述逻辑。
若将flex-wrap的值设为wrap-reverse,交叉轴方向会被反转。例如,当主轴为水平方向时(row或row-reverse),默认情况下新行会从上往下叠加,cross-start在顶部,cross-end在底部。而如果选用wrap-reverse,新行则从下往上排列,此时cross-start在底部,cross-end在顶部。
当弹性布局采用垂直主轴(如column或column-reverse)时,交叉轴默认是从左至右排列,新的一列弹性项目会插入在右侧;如果激活wrap-reverse,交叉轴方向则变为从右至左,新的项目列会向左添加,cross-start和cross-end在水平方向上也随之交换。
总之,主轴和交叉轴的方向、起止点以及尺寸的定义,完全取决于flex-direction和flex-wrap的组合,以及页面当前的文本书写模式。明确这些术语和关系,有助于我们灵活控制弹性布局的各种排列与交互方式。
在前面的案例中,我们虽然看到了弹性盒子(Flexbox)能够灵活地把项目一行一行排列起来,但还未深入解释这些项目在每一行中是如何定位和分布的。乍看之下,似乎所有元素自然而然会贴在行的起始端(main-start)——但这真的是唯一方式吗?它们能否平分剩余空间、或者均匀分散在整行上呢?
事实上,无论你用什么flex-flow组合,默认情况下,弹性盒子中的多余空间都会集中在主轴末端(main-end)和交叉轴末端(cross-end)。不过,CSS 提供了一系列属性,允许开发者按照自己的设计意图自由调整这些空间的分配和项目的排列方法。
通常情况下,当一行弹性项目没有完全填充整个容器时,这些项目会自动靠拢到主轴起点(main-start)。但弹性盒子规范为我们提供了多种项目对齐和空间分布的策略——它不仅可以使项目集中于主轴两端、居中或随机某一侧,还能让它们在空余空间间隔地自动分散。
除了主轴的排列,项目在交叉轴(纵向或横向,依据主轴方向不同)上的对齐方式同样可控。主要相关属性包括:
justify-content:决定每一行内项目在主轴方向上的分布方式;align-items:规定项目在每一行内交叉轴方向上的默认对齐方式(这一默认可在单个项目通过align-self覆写);align-content:当项目自动换行、形成多行时,决定所有行在交叉轴方向上的分布状态。justify-content 是调整弹性容器内项目沿主轴排列和多余空间处理方式的核心属性。它需在容器级别设定,而非针对单个项目。
justify-content支持多种取值:包括normal、start、end、center、flex-start、flex-end、left、right、space-between、space-around 和 space-evenly 等。默认是normal,而在 Flex 布局中一般等同于start。
start或flex-start,项目会紧贴主轴起点;end或flex-end,则项目全部靠向主轴终点;center,所有项目会聚集在主轴的中间;left和right则让项目依附于容器的左侧或右侧,无论主轴具体方向如何。更有意思的是空间分布型的取值:
space-between时,第一个项目靠在主轴起点,最后一个靠主轴末端,其余空白均匀地分布在各项目之间;space-evenly,每个项目之间及两端的空间均等分配,无论边缘还是项目之间,间距一致;space-around会将剩余空间等份,每个项目两边各分一半,导致项目之间的“缝隙”是边缘与项目之间的一倍大。通过合理配置这些属性,开发者能够精确掌控项目在弹性盒子中的排列与空白区域的分布,既能实现整齐对齐,也能轻松达到均匀分散、两端对齐等多种布局效果,满足各种视觉设计需求。
|<div class="container start"> <div class="item">项目1</div> <div class="item">项目2</div> <div class="item">项目3</div> </div> <div class="container center"> <
在弹性布局(Flexbox)中,除了可以通过justify-content属性控制项目在主轴(即横向或纵向)的排列方式之外,还可以借助align-items属性实现子项目在交叉轴上的对齐控制。需要注意,align-items同样设置在容器元素上,而不是单独针对某个子项目。
align-items支持包括normal、stretch、start、end、center、flex-start、flex-end、baseline等多种取值。默认值为normal,在Flexbox中实际等效于stretch。
当align-items设为默认值normal(即stretch)时,所有弹性项目会在交叉轴方向上自动拉伸,使它们的顶部与容器的起始边对齐、底部与容器的末尾边对齐。不论内容高矮多少,每个弹性项目都会自动填满容器在交叉轴上的空间,因此内容较少的元素也会被撑满。
如果你选择用center,弹性项目在交叉轴上的尺寸将根据内容自适应,而不会强行拉伸。此时,每个项目在容器中上下居中显示——其顶部和底部距离容器对应边界的间距相同。
而当设置为start、end、flex-start或flex-end等值时,弹性项目会分别靠近交叉轴的起始或结束方向贴边排列。这几种方式本质上都决定了项目对齐到容器的哪一条交叉轴边线,多种写法主要为兼容历史命名,具体差异可以参考官方文档。
需要注意的是,如果将项目靠交叉轴start或end排列时,项目本身的高度或宽度(视具体交叉轴而定)默认只会基于内容展开,不会多占空间。即使内容过多需要换行,也只是在项目内部分行;如果期望项目充满容器的整个交叉轴区域,应选择stretch。
若将align-items设为baseline,弹性容器会尝试让所有子项目的第一行文本基线对齐。当flex-direction为水平方向时(row 或 row-reverse),具体做法是所有项目会以最大的基线作为对齐标线,因此字号不同、内容不同的项目其基线也会对齐,看起来视觉上内容的底线呈一条水平线。
|<div class="container stretch"> <div class="item">项目1</div> <div class="item large">项目2<br/>更多内容</div> <div class="item">项目3</div> </div> <div class="container center"
有时我们希望仅部分弹性盒中的子元素在交叉轴上的对齐方式与其他项目不同,这时可以使用 CSS 的 align-self 属性。align-self 允许我们为每个弹性项目单独指定其在交叉轴上的对齐方式,从而覆盖父容器align-items的统一设置。
align-self 可设置的取值包括:auto、normal、stretch、baseline、center、start、end、flex-start、flex-end 等。其默认值为 auto,即采用父级容器的 align-items 设定,如果没有显式指定则行为与父级保持一致。
通过指定元素的 align-self 属性,无论父元素采用何种 align-items,都能精确调整该弹性项目在交叉轴上的独立对齐。需要注意的是,只能为标准元素或伪元素设置 align-self,对于匿名弹性项目(即弹性容器内的非空文本节点产生的匿名元素),该属性无法单独作用,始终遵循父弹性容器的 align-items。
虽然 align-items 默认为 stretch,但在下面的演示代码中我们会显式设置这一属性,这样就能清楚地演示第二个项目通过 align-self 获得了与其他兄弟元素不同的对齐效果。
|<div class="container"> <div class="item">项目1</div> <div class="item special">项目2</div> <div class="item">项目3</div> </div>
|.container {
在上述示例中,除了第二个弹性项目以外,所有弹性子项的 align-self 属性都采用了默认的 auto,这意味着它们会继承所在容器的 align-items 设置(这里是 stretch),因此在侧轴上的对齐方式完全取决于父容器。而第二个子项则赋予了不同的 align-self 值,使其展示个性化对齐效果。
跟前面提到的一样,align-items 能取的所有属性值,同样适用于 align-self,包括基线对齐(第一条、最后一条基线)、安全/不安全对齐等高级选项。
在大部分示例中,弹性容器在交叉轴(即垂直于主轴的方向)上的空间是自适应的——因为容器一般没有显式指定 block-size 或 height,默认行为为 height: auto,能自然地包住所有内容。
但如果显式给容器设定了高度或 block-size,可能会发现剩余空间出现在交叉轴尾部(cross-end),或者,内容会被挤压。这时,我们可以通过 CSS 的 align-content 属性,决定多行弹性内容在交叉轴上的整体分布方式。
align-content 的作用是:当弹性容器有多行(即项目换行显现多行)时,可以精准控制交叉方向上,行与行之间和行外侧的空间如何分配。尽管和 align-items 都处理“对齐”,但不同点在于——align-items 是单行内项目的对齐,而 align-content 负责多行整体之间的分布和对齐。
实际上,我们可以把 align-content 理解为类似于主轴上 justify-content 如何分布各项目,只不过 align-content 发生在交叉轴,面向“弹性行”(不是单个项目),只有当弹性容器产生多行时该属性才生效。对只包含一行的弹性容器或禁止换行场景,align-content 不会起作用。如只包含一行的弹性容器,其默认行为与 align-items 相同。
align-content 常用取值有:normal、stretch、center、start、end、flex-start、flex-end、space-between、space-around、space-evenly 等,默认值为 normal。
具体来说:当 align-content 取值为 normal、stretch、center、start、flex-start、end 或 flex-end 时,多余的空间主要被分布在行的外侧。这几种取值的表现与 align-items 类似。特别地,stretch 会拉伸所有行,使它们均分交叉轴的剩余空间,直到行之间和容器边缘完全贴合;而其他值一般是让行靠在某一侧,其余空间留在另一侧。
在 space-between、space-around 和 space-evenly 这些值中,弹性行之间会插入指定模式的间隔。举例来说,假设总共有 3em(120px)剩余空间:
space-between,所有相邻弹性行之间平分剩余空间,两两之间会各有 60px 间隔。space-around 时,剩余空间被平均分割为每行两侧(上、下)各一部分,每条弹性行的上方和下方各有 20px,不同行之间的距离则为 40px。space-evenly,总的剩余空间被等分为四份(用于三行,行之前和行之后也各有空隙),每份间隔为 30px,实现所有空隙全等。需要注意,stretch 在这里表现特别:当为该值时,额外的剩余空间不是留在行之间,而是直接叠加在每一行本身上,即各行沿交叉轴方向都会增高等量,使剩余空间被平分至每条行高。
Flexbox 下,弹性项目之间默认是没有明确的间隔的。虽然我们能够通过 justify-content 辅助分布空间或为每个项目设置 margin,但这些方案有时会带来副作用,如 margin 导致额外的换行,或者 justify-content: space-between 下项目头尾紧贴边缘,无法控制间距宽度。遇到此类需求时,CSS 提供了 gap 相关属性,能更直观灵活地定义项目间距。
具体而言,row-gap 和 column-gap 分别用于在弹性子项的行间与列间插入指定宽高的间隔,这通常被称为“排水沟”(gutter)。历史因素下,在 Flexbox、Grid 等布局模型下,normal 的默认值实际为 0 像素(即无间隔),在多栏布局里才是 1em。除此之外,也支持自定义长度或百分比数值。
gap 属性是 row-gap 和 column-gap 的简写。只填写一个值时,它会同时作为行和列的间隔;如果填写两个,第一个为行间距,第二个为列间距。
|<div class="gallery"> <div class="item">项目1</div> <div class="item">项目2</div> <div class="item">项目3</div> <div class="item">项目4</div> <div class
当我们在弹性布局中设置了gap间隙时,这个间距会添加在相邻弹性项目的外侧边缘之间。如果你还给弹性项目本身添加了margin,那每两个项目之间最终实际看到的空间就不仅仅是gap的大小,还会叠加上两边的边距宽度。
最初,gap这个属性只是在多栏布局(multi-column)中被引入,之后在CSS Grid布局中又扩展出了像grid-row-gap、grid-column-gap、grid-gap这样的更细粒度属性。随着标准的发展,这些间距属性最终变得可以被Grid、Flexbox和多栏目等各种布局模式共同支持。现代浏览器会自动把这些带连字符的旧属性当作新gap属性的等价实现,比如grid-gap在实际运作时就等价于gap。
弹性布局最核心的特征在于它可以让容器里的各个子元素根据空间的变化灵活地调整自身的宽度或高度,从而填充或适应主轴上(一般是水平或垂直方向)的可用空间。当父容器存在多余空间时,弹性项目可以按设置好的增长比例去分配这些空间;相反,如果空间不足,它们又能根据预设的收缩比例自动缩小,避免出现内容溢出。
在进行弹性布局时,我们通常会在子元素上设置 flex 属性。flex 是一个功能非常强大的简写属性,用来同时设置弹性项目的增长、收缩能力和基准尺寸。通过配置这三个值,我们可以灵活地控制弹性项目在不同情况下的尺寸变化:有多余空间时是否参与分配,空间不足时是否参与收缩,以及默认的基础尺寸是多少。
实际上,flex 其实是 flex-grow(增长系数)、flex-shrink(收缩系数)以及 flex-basis(基础尺寸)这三个属性的组合。虽然可以分别单独设置这三个属性,但在实际开发中建议直接使用 flex 简写形式,不仅语法简单,也更便于阅读和维护。
flex 属性可以接受多种形式的值,例如:none、initial、auto,或者按照 <flex-grow> <flex-shrink> <flex-basis> 的格式直接组合指定具体数值。如果未特别指定,则默认值为 0 1 auto,即不参与空间增长,但允许收缩,且基准尺寸为 auto。
flex 的本质作用是决定弹性项目沿主轴方向的尺寸表现。当元素被设为弹性项目时,flex 所指定的规则会优先生效,覆盖原本通过 width 或 height 等定义的主轴尺寸。三部分的含义如下:
其中,“弹性基础”决定了项目增长或收缩的起点。比如通过设置 flex-basis,可以规定每个弹性项目在没有多余空间或空间不足时的初始尺寸。如果希望弹性项目严格保持某个固定大小,可以将增长和收缩因子都设为 0,这样无论其他弹性项目如何变化,它的尺寸都不会随之调整。
|<div class="container"> <div class="item item1">项目1</div> <div class="item item2">项目2</div> <div class="item item3">项目3</div> </div>
|.container {
在上述CSS设计中,由于给弹性项目设置了200px的弹性基础,并且其增长和收缩系数都为0,所以这些弹性项目会严格维持在200像素宽,主轴为水平方向时,即使你设置了width: 50%,该宽度也不会生效。如果主轴为垂直方向,类似地,任何height的数值设置都会被覆盖。
这种弹性基础对width与height属性的覆盖,是在CSS级联和优先级体系之外起作用的。就算为width或height加上 !important,也无法覆盖flex-basis指定的主轴尺寸。
需要注意的是,只有弹性布局的子元素才会响应flex属性,如果目标并非弹性项目,设置该属性不会起任何作用。正确理解flex这个复合属性的三个组成部分,对于灵活地运用弹性盒模型布局至关重要。
flex-grow用于定义弹性项目在有剩余空间时的扩展比例。换句话说,它决定了弹性项目在父容器中“多出的空间”会由谁、以怎样的比例瓜分。
规范建议应尽量通过flex简写声明扩展因子,直接用flex-grow设置的做法并不推荐。这里为了说明原理,我们单独介绍此属性的用法。
flex-grow需为非负数值,且可以为小数。该值表示扩展比例,所有弹性项目的flex-grow值会相加,最终多余空间将按比例分配。例如:
flex-grow: 1,其余为0,则全部450px剩余空间归第三项所有,因此第三项宽度为550px,原本的width: 100px会被覆盖。flex-grow分别为1、1、3,总比例就是5,那么每1份为90px(450÷5)。结果分别为190px、190px、370px。如果弹性项目本身宽度各不相同,而扩展比也不一样,比如三个项目宽度为100px、250px、100px,对应flex-grow为1、1、3,总剩余空间为300px,则每份为60px。分配后分别变为160px、310px、280px,加起来正好750px满容器。
flex-shrink控制弹性项目在父容器空间不足时,各自“压缩”的比例。它也可以直接通过flex-shrink单独设置,当然规范仍建议合并到flex简写使用。
同样,规范更推荐在flex中声明收缩因子,单独写flex-shrink属性为讨论原理而用。
flex-shrink必须为非负数值,默认为1。收缩因子的数值决定了容器空间不足时,“哪一项”将收缩得更多,收缩的分配也是按比例进行。
举例说明:
正如我们前面所提到,弹性项目最终尺寸既受内容本身影响,也受盒子模型和flex属性影响。flex-basis是flex简写中的“主轴初始尺寸”,在决定分配扩展和收缩前,会先用这个值初始化弹性项目的大小。完整写法也可直接用flex-basis单独声明。
弹性基础实际上就是弹性项目初始尺寸基准,受box-sizing属性控制。块级元素在没参与弹性布局时,默认宽度主要由父级宽度、内容及其它盒模型属性共同决定,未声明宽高值时,宽度会随父级100%。
你可以用如300px、12%、5vw等与width或height同类单位声明弹性基础。如果设置为initial,弹性基础会被复原为auto。而auto的含义,会优先查找项目上的width或height属性值,如果未明确定义,则回退到依据内容自适应宽度,即类似content。因此,flex-basis经常决定了最终弹性项目的主轴尺寸,优先级高于直接设置的width或height。
除了长度和百分比之外,flex-basis还支持min-content、max-content、fit-content和content关键字。这些关键字为我们提供了基于内容的灵活尺寸控制。
当使用fit-content作为flex-basis的值时,浏览器会尽力平衡一行中的所有弹性项目,使它们在块尺寸上相似。考虑以下代码:
|.flex-item { flex-basis: 25%; width: auto; } .flex-item.fit { flex-basis: fit-content; }
设置 flex-basis: 25% 时,每个弹性项目会以父容器宽度的四分之一为基础进行分配,而 fit-content 则让每项根据自身内容自动调整宽度,内容多则宽,内容少则窄。其他如 content、max-content 和 min-content 关键字,也分别让弹性项目以内容实际、最大、最小宽度为基础,再结合弹性规则自动分配和适应容器空间。
合理运用这些关键字,可以实现布局项尺寸完全随内容自适应、或在空间充足时撑满、不足时自动收缩,无需精确计算每一项宽度,大大提升了 Flexbox 布局的灵活性与开发效率。
当 flex-basis 设为 auto 时,无论是默认还是手动设置,其显示逻辑通常是按照元素本身的主轴尺寸(例如宽度或高度)控制弹性项初始大小。如果子项上明确设置了 width 或 height,那 flex-basis 就以这个数值为准。例外情况出现于这些尺寸是 auto,这时弹性基础就退回到内容自适应(类似 content 效果)。
如果全部弹性项目尺寸都能放进容器,浏览器会保持原有宽度并不调整。如果溢出,则所有子项将基于其基础主轴尺寸按照各自收缩因子等比收缩——除非某一项 flex-shrink 为 0,不允许收缩。值得注意:如果像 inline-size, min-inline-size, width 或 min-width 这些属性都没设置,只用了 flex-basis: auto 或 flex: 0 1 auto,那项目宽度就是内容自适应宽度。
比如某个示例,3 个子项 flex-basis 都是 auto 且分别设置了 100、150、200 像素的宽度,如果总宽没超出容器,则展示宽度与声明一致;而如果宽度非常大(如 2000、3000、4000 像素),则由于超出容器,每项会自动按收缩规则缩小到合适宽度。
如果既没有给弹性项目设置 flex-basis,也没有配置 flex,浏览器会将主轴方向的尺寸理解为每个项目未弹性化前的固有宽度或高度。
一般来说,缺省下 flex-basis 是 auto,增长因子(grow)为 0,收缩因子(shrink)为 1。此时每项的弹性基础就是它具体的 width 或 height。假如三个项目宽为 100、200、300 像素,容器只有 540 像素宽,总宽大于容器,于是浏览器按比例收缩让它们正好塞进去。例如 600 缩到 540,等比缩小后得 90、180、270 像素。如果再换成 200、400、200 像素加起来 800,缩放后宽度依次变成 135、270、135 像素,始终保证总宽不超出容器。
除了默认的 auto 及关键字,flex-basis 还支持各种 CSS 长度单位(比如 px、em、rem、百分比等),用法类似于 width 或 height。如果你同时为弹性项设置了 flex-basis 和 width(或主轴为纵向的情况下的 height),则渲染时以 flex-basis 的数值为最终参考。举例来说,在第一个案例里,若写成
|flex-container { width: 540px; } item1 { width: 100px; flex-basis: 300px; /* flex: 0 1 300px; */ } item2 { width: 200px; flex-basis: 200px; /* flex: 0 1 200px; */ } item3 { width: 300
可以看到,最后生效的还是 flex-basis 的值,并且收缩时以其为依据。即便你定义了较小的基础,还受到 min-width, max-width 等其他盒模型属性约束,比如 flex-basis: 100px; min-width: 500px;,最终宽度至少会取 500px,而不会低于这个最小值。
|flex-container { width: 540px; } item1 { width: 100px; flex-basis: 300px; /* flex: 0 1 300px; */ } item2 { width: 200px; flex-basis: 200px; /* flex: 0 1 200px; */ } item3 { width: 300
当你为弹性盒项目设置了flex-basis,这个值会决定其“基础宽度”,从而覆盖掉其原先在主轴方向上的尺寸定义。例如,若分别给三个弹性项指定300px、200px、100px的基础宽度,并且容器宽度仅有540px,那么这三项就会按照比例被压缩(比如分别变为270px、180px、90px),以适应容器空间。如果容器本身的宽度没有限制,每个弹性项就会按照它们各自的flex-basis占据空间,即分别为300px、200px 和100px。
需要注意的是,flex-basis虽然可以优先决定弹性项的主轴尺寸,但它并不能突破诸如min-width、max-width、min-height、max-height等其他盒模型属性的约束。比如你设置了flex-basis: 100px但又给了min-width: 500px,最终实际宽度至少还是500px,基础宽度被下限限制住。
当flex-basis取值为百分比时,这个百分比是相对于弹性容器主轴的实际尺寸计算的。
请牢记:flex-basis使用百分比时,参照的是父级弹性容器的尺寸,而不是自身或内容的尺寸。
举个例子,假设有一个宽度为540px的弹性盒容器,前两个子项flex-basis和width都用到了auto,这其实等同于用内容宽度作为它们的基础宽度。假设内容宽度正好都是110px。第三项的flex-basis则设为100%,即设定其初始宽度为容器的全部宽度(540px)。但由于父容器内还有其他项,这个100%并不能保证第三项独占全部空间——除非它的弹性收缩因子为0(即不会被缩小),或者内容本身无法再被压缩,否则它最终的宽度会根据所有子项的基础宽度之和按比例分配。
在该场景下,三个项目的基础宽度合计为110 + 110 + 540 = 760px,但容器只有540px,所以需要压缩掉220px。我们按比例计算,每个项目都将变为原本宽度的71.05%(即1减去压缩比例28.95%):
这个计算成立的前提是每个弹性项本身没有不能再缩小的内容或被min-width等属性限制。只要内容能在这个宽度下换行,这些就是合理的最终宽度。如果某个子项内容无法折行且自身宽度超过压缩后所得的宽度,那么它和其他项还需要重新分配剩下的空间。
此外,例如在有些例子中,flex-basis: auto的弹性项可能会因为内容较多而自动换行多行。此时,最终布局会受到内容实际尺寸和潜在的CSS最小宽度限制共同影响。
|flex-container { width: 540px; } item1 { flex: 0 1 70%; } item2 { flex: 0 1 auto; } item3 { flex: 0 1 80%; }
这里我们给三个弹性项目分别设置了flex-basis基础值:第一个为70%,第二个为auto,第三个为80%。在本例中,auto代表未换行内容宽度(假设为110px),父级弹性容器宽度设定为540px。换算成具体数值后,三个项目的基础宽度分别为:
把这三个值加起来(378 + 110 + 432 = 920px),会发现它们的总宽度已经超出了弹性容器的实际宽度(540px)。这时,弹性盒会自动按比例压缩各个项目,使它们能够共同适应540px空间。压缩比例的计算方式是用容器宽度除以所有弹性项目原始基础宽度之和,即 540px ÷ 920px ≈ 0.587。也就是说,三个项目最终的宽度分别约为:
如果我们把容器宽度扩展到1000px,按同样的方式计算,弹性基础分别为700px(70% × 1000)、110px、800px(80% × 1000),总计1610px。实际宽度比例为 1000 ÷ 1610 ≈ 0.6211:
可以看到,由于项目1和项目3的flex-basis设定为70%和80%,总和始终大于100%,无论容器多大,三个项目总是处于需收缩状态。
值得注意的是,如果其中某一个弹性项目无法压缩(比如内容特别长或CSS中设置了flex-shrink: 0),如第一个项目仍为378px,其余空间(162px,540px-378px)就只能由剩下的两个项目分摊压缩。例如,如果项目2内容宽度大于理论分配值,比如说内容42px,比压缩所得32.88px要宽,那么分配结果会是378px、42px和120px(剩余空间)。
当弹性项目既没有指定flex-basis,也没有用到flex简写里的基础值部分时,其基础值默认是auto。如果是直接使用flex简写并且省略了第三个值,则基础值默认为0。需要特别留意的是,auto和0的弹性基础在行为上完全不同。
flex-basis: auto:项目的基础宽度为自身内容宽度(或有设置width/height则取对应属性值)。flex-basis: 0:所有项目的主轴剩余空间都按照flex-grow因子进行分配,完全不考虑内容的原始宽度,内容溢出会导致项目内部出现滚动条或溢出。举例来说,多个弹性项目都flex-basis: auto时,超出容器部分会参与收缩,未超出时,剩余空间按照增长因子分配给可增长的项目。如果设置为0,项目则是“从零开始”,所有空间由增长因子按比例划分。
min-width、max-width、min-height、max-height等约束会在这过程中对弹性项目的尺寸产生最终影响,防止它们被压缩或拉伸犯规。
例如,如果有三个弹性项目,每个内容原始宽度为110px,用flex: 1 1 auto时,假设有210px的剩余空间且增长因子总和为6,每个增长因子分得35px。三个项目宽度就分别为180px、145px、215px(约合内容宽度+增长分配空间)。
同样情况下,如果基础值为0(flex: 1 1 0),那么所有540px的空间完全按照增加因子分配,每个增长因子得到90px,比如:180px、90px、270px。如果内容本身就小于分到的空间,项目宽度会变窄,但不会低于其内容所需的最小宽度。
flex属性可以用简写形式快速设置三个相关属性,常用的几种预设有:
flex: initial:等价于flex: 0 1 auto,项目按内容或width/height占据空间,允许收缩,但不允许增长。flex: auto:等价于flex: 1 1 auto,允许弹性项目根据内容自动调整,并且可以收缩也可以增长填充剩余空间,是常用的“全弹性”方案。flex: none:等价于flex: 0 0 auto,弹性项目宽度只由自身内容或显式宽高决定,既不能拉伸也不能收缩(除非溢出时会遭遇布局问题)。flex: <正整数>:等价于flex: <数字> 1 0。此时所有弹性项目基础宽度为0,剩余空间按增长因子分配,不考虑内容宽度(内容宽度只会作为最小限制)。解释如下:
flex: initial时,项目不会增长,收缩因子为1,基础宽度为内容宽度或者显式设置的宽高。当空间不够时,项目会按比例缩小。对于显式宽度的情况,每个弹性项目基础宽度就是这个值,收缩也是等比例。flex: auto使项目既能缩小,也能扩大到填满空间。与initial不同的是有剩余空间会自动填充,弹性项目自动分配多余部分。flex: none禁止弹性项目进行弹性变化,导致内容可能会溢出父容器。flex: <number>,例如flex: 3,基础宽度为0,即“属性空间全部分配”,所有剩余空间依照增长因子划分,内容部分属下限,不做参与。只有当flex-grow大于0时,该项目才会获得空间。需要强调的是,flex弹性布局永远不会让项目宽度小于其内容(除非强制设置),如flex: 0项目,哪怕增长因子是0,项目也会保留实际内容宽度。
在使用 Flex 弹性布局时,容器中的各个弹性子项(flex item)会按照它们在 HTML 源码中出现的顺序进行排列,这种排列顺序默认与文档顺序一致。当然,主轴方向可以通过 flex-direction 进行反转,但如果我们希望更灵活地调整某些项目的展示顺序——比如将某个项目调整到最前面或最后面,无需修改 HTML 结构本身,这时就可以借助 order 属性来实现。
从规范来说,order 属性的初始值为 0。也就是说,所有未显式设置 order 的弹性项目,其顺序都为 0,这些项目根据它们在源码中的先后顺序沿主轴依次展示。如果需要让某些弹性项目出现在其它项目的前面或者后面,只需为这些项目设置不同的 order 数值即可。
具体来说:
order 的数值可以为负数、零或者正整数。order 的项目,会排在所有 order=0 的项目之前。order 的项目,则显示在所有 order=0 的项目之后。order 数值越小,项目越靠前;display: flex 的元素)的直接子元素有效,对非弹性子元素设置无效。需要再次强调,虽然页面上项目的排列顺序改变了,但实际的源代码顺序、辅助工具和键盘Tab键切换的顺序依然取决于 HTML 结构本身。
举个实际的例子,比如有 12 个弹性子项,我们希望第 7 个项目出现在最前面,第 6 个项目排在最后一位,可以按照以下方式为它们设置 order 属性:
|<ul> <li>项目1</li> <li>项目2</li> <li>项目3</li> <li>项目4</li> <li>项目5</li> <li class="last">项目6</li> <li class="first"
在这个示例中,我们特意为第6和第7个列表项指定了order属性,而其余的列表项则继续使用默认的order: 0。
order属性的数值越小,对应的项目就会越优先出现在容器的起始位置。由于第7个项目的order被设为-1,这是所有项目中最小的一个,所以它会被最先排列在最前面。而第6项因为被赋予了唯一的正值order: 1,在所有项目中是数值最大的,因此它会被排到弹性盒最后。至于其他项目,因为都没有显式设置order,所以都默认为0,这些项目会按照它们在HTML代码中的先后顺序自然排列在第一个和最后一个之间。
弹性容器会依据这些order值对所有项目进行排序。排序的规则是,先将所有项目分组,组内成员有相同的order值,不同组之间按照order数值从小到大排列。对于同一个order值的项目,则会优先展示HTML中靠前的元素顺序。通过这样的排序机制,即使HTML中的结构顺序没有改变,视觉上的排列依然可以通过CSS灵活调整。
使用弹性盒子布局时,可以高效地排列和对齐一组兄弟元素,而且适配各种布局场景和文本书写方向都非常灵活。Flexbox极大简化了父元素内部元素垂直、水平居中的实现,这类需求在Flexbox出现之前实现起来十分繁琐。
需要注意,Flexbox最适合用于一维方向的内容排列,也就是说它更适合在单一横向或纵向的轴线上组织元素。虽然它也可以做类似网格的排布,但对于严格的二维布局,Grid布局才是推荐的解决方案。尽管如此,Flexbox在实现导航栏、卡片列表、表单布局等常见场景时几乎无可替代。
通过这个部分的学习,相信你已经了解并掌握了Flexbox的基础原理和各种常用属性。接下来,可以尝试将这些知识应用到实际项目中,设计既灵活又具有良好适应性的页面布局。
需求: 创建一个简单的导航栏,要求:
HTML结构:
|<nav> <a href="/">首页</a> <a href="/about">关于</a> <a href="/products">产品</a> <a href="/contact">联系</a> </nav>
请写出CSS代码:
|nav { display: flex; justify-content: center; gap: 10px; padding: 10px; background-color: #f0f0f0; } nav a { padding: 10px 20px; background-color: #007bff; color
需求: 创建一个垂直侧边栏,要求:
HTML结构:
|<aside> <div class="menu-item">仪表板</div> <div class="menu-item">用户管理</div> <div class="menu-item">产品管理</div> <div class="menu-item">设置</div> </aside>
请写出CSS代码:
|aside { display: flex; flex-direction: column; width: 200px; padding: 20px; background-color: #f8f9fa; border-right: 1px solid #dee2e6; } .menu-item { padding: 15px;
需求: 创建一个卡片布局,要求:
HTML结构:
|<div class="card-container"> <div class="card"> <h3>卡片标题1</h3> <p>这是卡片的内容描述。</p> </div> <div class="card"> <h3>卡片标题2</h3> <p>这是卡片的内容描述,内容可能会比较长。</p>
请写出CSS代码:
|.card-container { display: flex; flex-wrap: wrap; gap: 20px; padding: 20px; } .card { flex: 1 1 300px; padding: 20px; background-color: #ffffff; border
需求: 使用Flexbox创建经典的圣杯布局,要求:
HTML结构:
|<div class="holy-grail"> <header>页眉</header> <div class="container"> <aside class="left-sidebar">左侧边栏</aside> <main>主要内容区域</main> <aside class="right-sidebar">右侧边栏</aside> </div>
请写出CSS代码:
|.holy-grail { display: flex; flex-direction: column; height: 100vh; } header, footer { background-color: #343a40; color: white; padding: 20px; text-align: center; }
需求: 创建一个图片画廊,要求:
HTML结构:
|<div class="gallery"> <img src="image1.jpg" alt="图片1"> <img src="image2.jpg" alt="图片2"> <img src="image3.jpg" alt="图片3"> <img src="image4.jpg" alt="图片4"> <
请写出CSS代码:
|.gallery { display: flex; flex-wrap: wrap; gap: 15px; padding: 20px; background-color: #f8f9fa; } .gallery img { flex: 1 1 200px; height: 150px;
nav {
display: flex;
border-bottom: 1px solid #ccc;
}
a {
margin: 0 5px;
padding: 5px 15px;
border-radius: 3px 3px 0 0;
background-color: #ddaa00;
text-decoration: none;
color: #ffffff;
}
a:hover,
a:focus,
a:active {
background-color: #ffcc22;
color: black;
}|.container { display: flex; width: 400px; border: 2px solid #ccc; padding: 10px; gap: 10px; } /* nowrap - 不换行,项目会溢出 */ .container.nowrap { flex-wrap: nowrap; } /* wrap - 换行,新行在下方 */ .container.wrap { flex-wrap: wrap; } /* wrap-reverse - 反向换行,新行在上方 */ .container.wrap-reverse { flex-wrap: wrap-reverse; } .item { background-color: #ddaa00; color: white; padding: 20px; min-width: 150px; text-align: center; }
|.container { display: flex; width: 500px; border: 2px solid #ccc; padding: 10px; margin-bottom: 20px; gap: 10px; } .start { justify-content: flex-start; } .center { justify-content: center; } .space-between { justify-content: space-between; } .space-around { justify-content: space-around; } .space-evenly { justify-content: space-evenly; } .item { background-color: #ddaa00; color: white; padding: 15px; border-radius: 5px; }
|.container { display: flex; height: 200px; border: 2px solid #ccc; padding: 10px; margin-bottom: 20px; gap: 10px; } .stretch { align-items: stretch; } .center { align-items: center; } .baseline { align-items: baseline; } .item { background-color: #ddaa00; color: white; padding: 15px; border-radius: 5px; } .large { font-size: 1.5em; padding: 20px; }
|.gallery { display: flex; flex-wrap: wrap; gap: 20px; border: 2px solid #ccc; padding: 10px; width: 500px; } .item { background-color: #ddaa00; color: white; padding: 20px; min-width: 150px; text-align: center; }
|ul { display: flex; list-style: none; padding: 0; gap: 10px; } li:nth-of-type(6) { order: 1; } li:nth-of-type(7) { order: -1; }