做前端的,谁没被float布局折磨过?
我还记得刚学CSS的时候,布局全靠float。要做个两栏布局?左边float:left,右边float:right,然后父元素overflow:hidden清除浮动。要做个导航栏?li全部float:left,然后ul overflow:hidden。要做个卡片列表?每个卡片float:left,然后算宽度、算margin、算一行放几个,最后一行还经常对不齐。
float布局的问题,说多了都是泪:
- 父元素高度塌陷,必须清除浮动(overflow:hidden或者clearfix)
- 居中难,垂直居中更是难上加难
- 等高列难,两栏内容不一样高就对不齐底部
- 排列顺序不灵活,想调换顺序只能改HTML
- 响应式麻烦,不同屏幕尺寸要算各种宽度
- 一个元素float了,后面的元素都要处理,牵一发而动全身
2016年,Flexbox(弹性盒子布局)终于被主流浏览器全面支持了。Chrome、Firefox、Safari、Edge都支持,甚至IE11也支持(虽然有一些bug)。我在项目中开始尝试用Flexbox,用了之后就再也回不去了——原来布局可以这么简单、这么优雅。
今天就来聊聊Flexbox,从float的痛点到Flexbox的用法,从核心概念到实际案例,分享一下我这半年多使用Flexbox的真实体验。
一、float布局的那些痛
在聊Flexbox之前,先回忆一下float布局给我们带来的痛苦。如果你是前端老兵,这些场景一定很熟悉;如果你是新手,恭喜你,Flexbox可以让你避开这些坑。
1. 父元素高度塌陷
这是float最经典的问题。子元素float之后,脱离了文档流,父元素"撑不起来",高度变成0。
<div class="parent">
<div class="child" style="float:left;">子元素1</div>
<div class="child" style="float:left;">子元素2</div>
</div>父元素的高度是0!因为子元素都float了,脱离了文档流,父元素"看不到"它们。
解决方案:
- 父元素
overflow:hidden(但是会隐藏溢出内容) - 父元素也float(但是会影响其他元素)
- 用clearfix伪元素(最常用,但是要写一堆CSS)
.clearfix::after {
content: '';
display: block;
clear: both;
}每一个有float子元素的父元素,都要加clearfix。烦不烦?
2. 垂直居中难
CSS里,水平居中简单(margin:0 auto或者text-align:center),但是垂直居中?那是前端界的"世纪难题"。
float布局下,垂直居中的方法:
- 知道高度:用绝对定位+margin-top负一半
- 不知道高度:用display:table-cell(但是要改变HTML结构)
- 用line-height(只适用于单行文本)
- 用CSS3 transform(但是兼容性和性能有问题)
每一种方法都有局限性,都不优雅。一个简单的垂直居中,要写一堆hack,这合理吗?
3. 等高列难
两栏布局,左边内容少,右边内容多,结果左边短右边长,底部对不齐。要做等高列?
float布局下的方法:
- 用背景图模拟(假等高,不是真等高)
- 用padding-bottom+margin-bottom负(hack,而且有副作用)
- 用JS算高度(性能差,而且JS加载前不对)
- 用display:table(改变布局方式,不灵活)
每一种都是hack,没有一种是"正解"。
4. 排列顺序不灵活
float布局下,元素的排列顺序由HTML顺序决定。想在大屏幕上左A右B,小屏幕上B在上A在下?只能用JS调换DOM顺序,或者写两套HTML。
响应式布局下,这种需求很常见,但是float布局处理起来非常麻烦。
5. 响应式计算麻烦
做响应式卡片列表,一行放几个?每个卡片宽度多少?margin多少?要算半天,而且不同屏幕尺寸要算不同的值,媒体查询写一堆。
.card {
float: left;
width: 31%;
margin-right: 3.5%;
}
.card:nth-child(3n) {
margin-right: 0;
}
@media (max-width: 768px) {
.card {
width: 48%;
margin-right: 4%;
}
.card:nth-child(3n) {
margin-right: 4%;
}
.card:nth-child(2n) {
margin-right: 0;
}
}算宽度、算margin、清除最后一列的margin,烦不胜烦。
6. 一个float,处处要处理
最烦的是,一个元素float了,后面的元素都要处理。父元素要清除浮动,后面的元素要clear,布局被打乱了要调整。牵一发而动全身,改一个地方可能影响好几个地方。
float本来是用来做"文字环绕图片"的,结果被我们拿来做整个页面的布局,这本身就是"滥用"。Flexbox的出现,就是为了让布局回归正途。
二、Flexbox的核心概念
Flexbox(Flexible Box,弹性盒子)是CSS3的一种布局模式,专门为布局设计。它的核心思想是:让容器有能力让其子项目能够改变其宽度、高度(甚至顺序),以最佳方式填充可用空间。
Flexbox有两个核心概念:Flex容器(Flex Container)和Flex项目(Flex Item)。
1. Flex容器和Flex项目
<div class="container">
<div class="item">项目1</div>
<div class="item">项目2</div>
<div class="item">项目3</div>
</div>.container {
display: flex; /* 或者 display: inline-flex */
}.container就是Flex容器,设置display:flex后,它的直接子元素就变成了Flex项目.item就是Flex项目,它们会按照Flexbox的规则排列
注意:只有直接子元素才是Flex项目,孙子元素不受影响。
2. 主轴和交叉轴
Flexbox有两个轴:主轴(Main Axis)和交叉轴(Cross Axis)。
- 主轴:Flex项目排列的方向(默认是水平方向,从左到右)
- 交叉轴:垂直于主轴的方向(默认是垂直方向,从上到下)
主轴的方向由flex-direction属性决定:
row(默认):主轴水平,从左到右row-reverse:主轴水平,从右到左column:主轴垂直,从上到下column-reverse:主轴垂直,从下到上
理解主轴和交叉轴非常重要,因为很多Flexbox属性都是针对主轴或交叉轴的。
3. Flexbox的优势
Flexbox相比float布局的优势:
- 不需要清除浮动:Flex容器自动包裹子元素,不会高度塌陷
- 垂直居中简单:一行代码搞定垂直居中
- 等高列天然支持:Flex项目默认等高
- 排列顺序灵活:用
order属性可以随意调整顺序,不用改HTML - 响应式简单:用
flex-wrap和媒体查询,响应式布局非常简单 - 对齐方式丰富:主轴、交叉轴都有多种对齐方式
- 空间分配灵活:用
flex-grow、flex-shrink、flex-basis可以灵活分配空间
三、Flexbox常用属性详解
Flexbox的属性分为两类:容器属性(设置在Flex容器上)和项目属性(设置在Flex项目上)。
1. 容器属性
flex-direction:主轴方向
.container {
flex-direction: row; /* 默认,水平从左到右 */
flex-direction: row-reverse; /* 水平从右到左 */
flex-direction: column; /* 垂直从上到下 */
flex-direction: column-reverse; /* 垂直从下到上 */
}flex-wrap:是否换行
.container {
flex-wrap: nowrap; /* 默认,不换行,项目会被压缩 */
flex-wrap: wrap; /* 换行,超出的项目换到下一行 */
flex-wrap: wrap-reverse; /* 换行,但是从下往上排 */
}这是做响应式卡片列表的关键!设置flex-wrap:wrap后,项目超出容器宽度会自动换行,不需要算宽度和margin。
flex-flow:flex-direction和flex-wrap的简写
.container {
flex-flow: row wrap; /* 等同于 flex-direction:row; flex-wrap:wrap; */
}justify-content:主轴对齐方式
.container {
justify-content: flex-start; /* 默认,左对齐(主轴起点) */
justify-content: flex-end; /* 右对齐(主轴终点) */
justify-content: center; /* 居中 */
justify-content: space-between; /* 两端对齐,项目之间间隔相等 */
justify-content: space-around; /* 每个项目两侧间隔相等 */
justify-content: space-evenly; /* 所有间隔相等(包括两端) */
}这是做水平居中、两端对齐导航栏的关键!
align-items:交叉轴对齐方式
.container {
align-items: stretch; /* 默认,项目拉伸填满交叉轴(等高列!) */
align-items: flex-start; /* 顶部对齐 */
align-items: flex-end; /* 底部对齐 */
align-items: center; /* 垂直居中! */
align-items: baseline; /* 基线对齐 */
}看到了吗?align-items:center就是垂直居中!一行代码搞定,再也不用写hack了。align-items:stretch就是等高列,天然支持!
align-content:多行对齐方式(只有一行时无效)
.container {
align-content: stretch; /* 默认,每行拉伸填满 */
align-content: flex-start; /* 顶部对齐 */
align-content: flex-end; /* 底部对齐 */
align-content: center; /* 居中 */
align-content: space-between; /* 两端对齐 */
align-content: space-around; /* 每行两侧间隔相等 */
}2. 项目属性
order:项目排列顺序
.item {
order: 0; /* 默认,数值越小越靠前,可以是负数 */
}
.item-1 { order: 2; }
.item-2 { order: 1; } /* 这个会排在前面 */
.item-3 { order: 3; }响应式布局神器!大屏幕上A在左B在右,小屏幕上想让B在上A在下?给B设置order:-1就行了,不用改HTML,不用JS。
flex-grow:项目放大比例
.item {
flex-grow: 0; /* 默认,不放大 */
}
.item-1 { flex-grow: 1; }
.item-2 { flex-grow: 2; } /* 这个会占更多空间 */如果容器有剩余空间,项目会按flex-grow的比例分配剩余空间。所有项目flex-grow都是1的话,平均分配剩余空间。
flex-shrink:项目缩小比例
.item {
flex-shrink: 1; /* 默认,空间不足时会缩小 */
}
.item-1 { flex-shrink: 0; } /* 这个不会缩小 */如果容器空间不足,项目会按flex-shrink的比例缩小。设置flex-shrink:0的项目不会缩小,保持原始宽度。
flex-basis:项目初始大小
.item {
flex-basis: auto; /* 默认,项目的原始大小 */
flex-basis: 200px; /* 初始宽度200px */
flex-basis: 50%; /* 初始宽度50% */
}flex-basis设置项目在分配剩余空间之前的初始大小。
flex:flex-grow、flex-shrink、flex-basis的简写
.item {
flex: 0 1 auto; /* 默认,等同于 flex-grow:0; flex-shrink:1; flex-basis:auto; */
flex: 1; /* 等同于 flex-grow:1; flex-shrink:1; flex-basis:0%; 平均分配空间 */
flex: none; /* 等同于 flex-grow:0; flex-shrink:0; flex-basis:auto; 不放大不缩小 */
flex: auto; /* 等同于 flex-grow:1; flex-shrink:1; flex-basis:auto; 自动分配 */
}这是最常用的简写,flex:1表示项目平均分配容器空间,做自适应布局非常方便。
align-self:单个项目的交叉轴对齐方式(覆盖容器的align-items)
.item {
align-self: auto; /* 默认,继承容器的align-items */
align-self: flex-start; /* 顶部对齐 */
align-self: flex-end; /* 底部对齐 */
align-self: center; /* 居中 */
align-self: stretch; /* 拉伸 */
align-self: baseline; /* 基线对齐 */
}某个项目想单独对齐?用align-self覆盖容器的设置。
四、实际布局案例
光说不练假把式,下面用几个实际案例看看Flexbox有多好用。
1. 水平垂直居中
这是前端最常见的需求之一,Flexbox一行代码搞定:
<div class="container">
<div class="box">我是居中的盒子</div>
</div>.container {
display: flex;
justify-content: center; /* 主轴居中(水平) */
align-items: center; /* 交叉轴居中(垂直) */
height: 300px; /* 容器要有高度才能看出垂直居中 */
}就这么简单!不需要绝对定位,不需要transform,不需要table-cell,两行CSS搞定水平垂直居中。
2. 导航栏
经典的导航栏布局:Logo在左,导航链接在右,链接之间有间隔。
<nav class="navbar">
<div class="logo">Logo</div>
<ul class="nav-links">
<li><a href="#">首页</a></li>
<li><a href="#">文章</a></li>
<li><a href="#">关于</a></li>
</ul>
</nav>.navbar {
display: flex;
justify-content: space-between; /* Logo左,链接右 */
align-items: center; /* 垂直居中 */
padding: 0 20px;
height: 60px;
}
.nav-links {
display: flex; /* 链接也用Flexbox */
list-style: none;
margin: 0;
padding: 0;
gap: 20px; /* 链接之间间隔20px(gap是Flexbox的好东西) */
}不需要float,不需要clearfix,不需要算margin,导航栏就做好了。而且垂直居中天然对齐,不管Logo多高、链接多高,都能居中对齐。
3. 等高两栏布局
左边侧边栏,右边主内容,两栏等高(不管哪边内容多)。
<div class="layout">
<aside class="sidebar">侧边栏</aside>
<main class="content">主内容</main>
</div>.layout {
display: flex;
}
.sidebar {
width: 250px;
flex-shrink: 0; /* 侧边栏不缩小,固定250px */
}
.content {
flex: 1; /* 主内容占满剩余空间 */
}就这么简单!两栏天然等高(因为align-items默认是stretch),侧边栏固定宽度,主内容自适应。不需要背景图模拟等高,不需要JS算高度,不需要padding-bottom hack。
4. 响应式卡片列表
卡片列表,大屏幕一行4个,中屏幕一行3个,小屏幕一行2个,超小屏幕一行1个。
<div class="card-list">
<div class="card">卡片1</div>
<div class="card">卡片2</div>
<div class="card">卡片3</div>
<!-- ...更多卡片 -->
</div>.card-list {
display: flex;
flex-wrap: wrap; /* 允许换行 */
gap: 20px; /* 卡片之间间隔20px */
}
.card {
flex: 1 1 calc(25% - 15px); /* 一行4个,减去gap的影响 */
min-width: 200px; /* 最小宽度,防止太小 */
}
@media (max-width: 992px) {
.card { flex: 1 1 calc(33.333% - 13px); } /* 一行3个 */
}
@media (max-width: 768px) {
.card { flex: 1 1 calc(50% - 10px); } /* 一行2个 */
}
@media (max-width: 480px) {
.card { flex: 1 1 100%; } /* 一行1个 */
}用flex-wrap:wrap和gap,卡片自动换行,间隔均匀,不需要算margin、不需要nth-child清除最后一列的margin。响应式只需要改flex-basis的百分比,非常简洁。
5. 底部固定(Sticky Footer)
页面内容不够多时,页脚贴在底部;内容多时,页脚被内容推下去。
<div class="page">
<header>头部</header>
<main class="content">内容</main>
<footer>页脚</footer>
</div>.page {
display: flex;
flex-direction: column; /* 垂直排列 */
min-height: 100vh; /* 至少占满视口高度 */
}
.content {
flex: 1; /* 内容区占满剩余空间,把页脚推到底部 */
}经典的Sticky Footer布局,Flexbox几行代码搞定。不需要绝对定位,不需要calc,不需要给内容区加padding-bottom。
6. 输入框+按钮组合
输入框占满剩余空间,按钮固定宽度在右边。
<div class="search-box">
<input type="text" placeholder="搜索...">
<button>搜索</button>
</div>.search-box {
display: flex;
}
.search-box input {
flex: 1; /* 输入框占满剩余空间 */
}
.search-box button {
flex-shrink: 0; /* 按钮不缩小 */
}输入框自适应宽度,按钮固定在右边,不需要float,不需要算宽度。
五、浏览器兼容性
2016年,Flexbox的兼容性已经非常好了:
- Chrome 29+(2013年)
- Firefox 28+(2014年)
- Safari 9+(2015年,iOS 9+)
- Edge 12+(2015年)
- IE 11(部分支持,有一些bug)
IE10及以下不支持(IE10支持旧版语法,需要加前缀)。
如果需要兼容IE11,需要注意:
- IE11不支持
gap属性(用margin代替) - IE11的
flex-basis有bug,有时候需要显式设置宽度 - IE11的
min-height和Flexbox配合有问题 - 有些属性需要加
-ms-前缀
2016年,大部分项目已经不需要兼容IE10及以下了,IE11的bug也有workaround。如果你的项目不需要兼容IE,那Flexbox可以放心大胆地用。
可以用Autoprefixer自动添加前缀,不需要手动写。
六、Flexbox的最佳实践和坑
用了半年多Flexbox,总结了一些最佳实践和需要注意的坑:
最佳实践:
- 优先用Flexbox做一维布局:Flexbox最适合做一维布局(一行或一列),二维布局(行和列都要控制)可以用CSS Grid(2016年还不太成熟)。
- 用gap做间隔:
gap属性比margin更简洁,不会有最后一列margin的问题。(IE11不支持,需要兼容的话用margin) - 用flex简写:
flex:1比分别写flex-grow、flex-shrink、flex-basis更简洁。 - 容器和项目属性分清楚:哪些属性设置在容器上,哪些设置在项目上,不要搞混。
- 善用order做响应式:响应式布局需要调整顺序时,用
order而不是改HTML或JS。 - 移动端优先:先写移动端的Flexbox布局,再用媒体查询适配大屏幕。
需要注意的坑:
- Flex项目默认不缩小到内容以下:
flex-shrink:1默认会缩小,但是不会缩小到内容以下。如果内容很长,项目可能不会按预期缩小。可以设置min-width:0或overflow:hidden解决。 - Flex项目的margin不会合并:和普通块级元素不同,Flex项目的margin不会合并,这通常是好事,但有时候会和预期不同。
- absolute定位的子元素不是Flex项目:如果子元素设置了
position:absolute,它就脱离了Flexbox布局,不是Flex项目了。 - Flex容器的
column-count无效:Flex容器上设置column-count(多列布局)会无效。 - IE11的各种bug:如果需要兼容IE11,要多测试,很多属性在IE11上表现不同。
- 不要滥用Flexbox:Flexbox不是万能的,简单的布局(如单个元素居中)可以用更简单的方法,不需要都套Flexbox。
七、总结
用了Flexbox之后,我真的再也回不去float布局了。原来布局可以这么简单、这么优雅、这么符合直觉。
Flexbox解决了float布局的所有痛点:
- 不需要清除浮动(父元素不会高度塌陷)
- 垂直居中一行代码搞定
- 等高列天然支持
- 排列顺序灵活(order属性)
- 响应式简单(flex-wrap + 媒体查询)
- 对齐方式丰富(justify-content、align-items)
- 空间分配灵活(flex-grow、flex-shrink、flex-basis)
Flexbox核心概念:
- Flex容器和Flex项目
- 主轴和交叉轴
- 容器属性:flex-direction、flex-wrap、flex-flow、justify-content、align-items、align-content
- 项目属性:order、flex-grow、flex-shrink、flex-basis、flex、align-self
实际案例:水平垂直居中、导航栏、等高两栏、响应式卡片、Sticky Footer、输入框+按钮组合,Flexbox都能简洁优雅地实现。
2016年,Flexbox的兼容性已经非常好,主流浏览器都支持,IE11有一些bug但是有workaround。大部分项目都可以放心使用Flexbox。
如果你还在用float布局,我强烈建议你试试Flexbox。一开始可能需要适应一下主轴、交叉轴这些概念,但是一旦上手,你会发现布局从来没有这么简单过。用了Flexbox之后,你也会和我一样,再也回不去float布局了。
最后,Flexbox不是终点,CSS Grid(二维布局)已经在来的路上了,未来的CSS布局会越来越强大。但是在2016年这个时间点,Flexbox就是最好用的布局工具,没有之一。
愿每一个前端开发者都能摆脱float的折磨,享受Flexbox带来的优雅布局。写CSS,本来就应该是一件愉快的事情。
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录