这一篇文章主要是实现一些Flexbox
布局的一些小方案,网上关于Flexbox
的文章有非常多,所以不会过多的讲解Flexbox
的基础知识,如果你对Flexbox
还不是很了解,可以先去看看MDN Flexbox的文档;
1. Flexbox + margin
Flexbox
+margin
的组合布局方案是一个非常实用的方案,我把它放在第一位,因为这个方案在实际开发中使用的非常多;
1.1. 居中对齐
<div class="flex-container">
<div class="flex-item">
Flex item
</div>
</div>
.flex-container {
display: flex;
width: 100vw;
height: 100vh;
background: #d7dbdd;
}
.flex-item {
padding: 20px;
border-radius: 4px;
background: #f7dc6f;
margin: auto;
}
这个方案是实现一个元素在容器中水平垂直居中,只需要给这个元素设置margin: auto
即可,这个方案大家应该都不陌生,就不过多讲解了;
需要注意的是实现居中对齐的前提是容器内只有一个元素,如果容器内有多个元素,那么这样设置会因为多出来的元素占用分配空间,导致伪居中,利用这个特性也可以做很多事情,后面会提到;
tips: 如果只需要水平居中,其实用不到
Flexbox
,只需要给元素的width
设置一个值,如果需要这个元素的大小随着内容的变化而变化,那么width
的值可以是fit-content
,可以参考如下代码:
.item { width: fit-content; margin: auto; }
注:外层容器不需要设置
display: flex
;
1.2. 部分元素靠右对齐
<div class="wrap">
<h3>最后一个元素靠右</h3>
<div class="flex-container">
<div class="flex-item">Flex item1</div>
<div class="flex-item">Flex item2</div>
<div class="flex-item" style="margin-left: auto;">Flex item3</div>
</div>
</div>
<div class="wrap">
<h3>其余元素靠右</h3>
<div class="flex-container">
<div class="flex-item">Flex item1</div>
<div class="flex-item" style="margin-left: auto;">Flex item2</div>
<div class="flex-item">Flex item3</div>
</div>
</div>
.flex-container {
display: flex;
gap: 10px;
background: #d7dbdd;
}
.flex-item {
padding: 20px;
background: #f7dc6f;
border: 1px solid #f39c12;
border-radius: 4px;
}
这个方案是实现一个元素在容器中靠右对齐,只需要给这个元素设置margin-left: auto
即可,如果需要右对齐的元素不是最后一个元素,那么跟在这个元素后面的元素也会被挤到右边;
tips: 如果只需要第一个元素在左边,那么只需要将第二个元素设置
margin-left: auto
即可;如果需要所有的元素都靠右对齐,那么建议使用
justify-content: flex-end
;
1.3. 中间元素居中,两边元素靠边
<div class="wrap">
<h3>中间元素居中,两边元素靠边</h3>
<div class="flex-container">
<div class="wrap-0w">
<div class="wrap-0w__flex-container">
<div class="flex-item">Flex item1</div>
</div>
</div>
<div class="flex-item" style="margin: auto;">Flex item2</div>
<div class="wrap-0w">
<div class="wrap-0w__flex-container" style="transform: translateX(-100%);">
<div class="flex-item">Flex item3</div>
<div class="flex-item">Flex item4</div>
</div>
</div>
</div>
<div class="flex-container">
<div class="wrap-0w">
<div class="wrap-0w__flex-container">
<div class="flex-item">Flex item1</div>
<div class="flex-item">Flex item2</div>
</div>
</div>
<div class="flex-item" style="margin: auto;">Flex item3</div>
<div class="wrap-0w">
<div class="wrap-0w__flex-container" style="transform: translateX(-100%);">
<div class="flex-item">Flex item4</div>
</div>
</div>
</div>
</div>
.flex-container {
display: flex;
gap: 10px;
background: #d7dbdd;
}
.flex-container + .flex-container {
margin-top: 10px;
}
.flex-item {
padding: 20px;
background: #f7dc6f;
border: 1px solid #f39c12;
border-radius: 4px;
}
.wrap-0w {
width: 0;
gap: inherit;
}
.wrap-0w .wrap-0w__flex-container {
width: max-content;
display: flex;
flex-wrap: nowrap;
gap: inherit;
}
如果我们希望其中一个元素完全居中,左右两边元素靠边,这种情况Flexbox
是无法直接实现的,因为Flexbox
只能分配剩余空间,这个时候就需要对元素层级做一些调整;
我上面的例子就是需要将左右两边的元素放到一个0宽
的容器中,然后这个容器里面还需要一个容器,需要将这个容器的宽度设置为max-content
,这样这个容器就会被内容撑开,从而展示完整的内容,最右边的元素需要设置transform: translateX(-100%)
,这样就可以将这个元素的位置摆放正确;
因为里层的容器成为了一个新的容器,很多样式需要从父容器中继承,如果父容器的样式发生变化,这个内层的容器也需要跟着变化,为了减少这种重复的工作,可以使用css
的继承特性,这样就可以减少很多重复的代码,例如我这里的gap
;
不过缺点也很明显,是通过补丁的方式实现的,很多人会觉得你这种情况我使用定位布局
来做了,我这里不评价哪种方案更好,只是提供一种Flexbox
的解决方案;
tips: 也可以通过控制两侧元素的宽度来实现,将两侧元素的宽度固定为相同小大,那么中间元素可分配的空间就是固定的,这样就可以实现中间元素居中,两侧元素靠边;
相对于上面的方案,这种方案更加简单,缺点就是可能中间元素可支配的空间不够大,拿上面的案例来说,左右的元素数量不一致,数量少的一侧使用过多的空间,导致中间元素的空间过小;
2. 分配剩余空间
作为一个弹性布局,Flexbox
最大的特点就是可以分配剩余空间,因为在web
页面中,通常宽度是有限且会随着页面的大小变化而变化的,空间发生变化之后,我们希望元素的大小也能够随之变化,这就是响应式布局的一种体现;
2.1 文字区域动态溢出省略号
<div class="wrap">
<h3>文字区域动态溢出省略号</h3>
<div class="flex-container">
<div class="flex-item" style="flex-shrink: 0;">Label:</div>
<div class="flex-item text-overflow" style="flex-grow: 1;">这里是文字这里是文字这里是文字这里是文字这里是文字这里是文字这里是文字</div>
</div>
<div class="flex-container">
<div class="flex-item" style="flex-shrink: 0;">不同长度的label:</div>
<div class="flex-item text-overflow" style="flex-grow: 1;">这里是文字这里是文字这里是文字这里是文字这里是文字这里是文字这里是文字</div>
<div class="flex-item" style="flex-shrink: 0;">小图标</div>
</div>
<div class="flex-container">
<div class="flex-item" style="flex-shrink: 0; width: 5em;">需要label对齐就对每一项设置相同的宽度:</div>
<div class="flex-item text-overflow" style="flex-grow: 1;">这里是文字这里是文字这里是文字这里是文字这里是文字这里是文字这里是文字</div>
<div class="flex-item" style="flex-shrink: 0;">小图标</div>
</div>
<div class="flex-container">
<div class="flex-item text-overflow" style="flex-shrink: 0; width: 5em;">需要label对齐就对每一项设置相同的宽度:</div>
<div class="flex-item text-overflow" style="flex-grow: 1;">这里是文字这里是文字这里是文字这里是文字这里是文字这里是文字这里是文字</div>
<div class="flex-item" style="flex-shrink: 0;">小图标</div>
</div>
</div>
关键的css
代码都写到了行内样式中,主要就两个属性flex-grow
和flex-shrink
,flex-grow
是用来分配剩余空间的,flex-shrink
是用来收缩空间的;
默认情况下flex-grow
的值是0
,不会分配剩余空间,flex-shrink
的值是1
,会收缩空间;
在上面的示例中,我们需要设置flex-grow: 1
来让元素分配剩余空间,flex-shrink: 0
来阻止元素收缩空间,这样标签的文字就会完整的显示不会被压缩,对于文字区域,我们还可以使用text-overflow: ellipsis
来实现文字溢出省略号;
tips: 在文字显示省略号的时候,通常是需要为文字区域设置一个固定的宽度,但是这样做布局就不是很灵活,而使用
Flexbox
就可以很好的解决这个问题;同时为了布局的美观可能需要所有的
label
对齐,因为label
的长度可能不一样,使用Flexbox
并不能很好的解决这个问题,这个时候就需要为每一个label
设置相同的宽度作为补丁,需要解决这个问题可以使用Grid
布局;
2.2 内容超出滚动
<div class="wrap">
<h3>内容超出滚动</h3>
<div class="flex-container">
<div class="flex-item" style="flex-shrink: 0;">Logo</div>
<div class="flex-item menu" style="flex-grow: 1;">
<ul style="display: flex; overflow: auto;">
<li>菜单1</li>
<li>菜单2</li>
<li>菜单3</li>
<li>菜单4</li>
<li>菜单5</li>
<li>菜单6</li>
</ul>
</div>
</div>
</div>
有时候我们在做一个菜单的时候,菜单的高度是固定的,但是菜单的内容可能会超出这个高度,这个时候我们希望菜单的内容可以滚动,但是在内容少的时候我们希望可以不显示滚动条,在内容多的时候自动显示滚动条;
核心原理还是一样的,只需要对菜单的内容设置flex-grow: 1
,这样菜单的内容就会占据剩余空间,然后设置overflow: auto
,这样就可以实现内容超出滚动的效果,别忘了给其他区域设置flex-shrink: 0
,防止内容收缩;
这一块的原理和上面的文字区域动态溢出省略号是一样的,如果需要让元素出现滚动条也是要固定元素的宽/高度(大小),然后设置overflow: auto
,使用Flexbox
就可以动态的确定元素的大小;
tips: 核心代码只有两行,写在
ul
上,display: flex; overflow: auto;
,这样就可以实现菜单的滚动效果;示例代码使用了
ul
作为菜单的容器,因为需要让元素出现横向混动,所以需要改变文档流,使用display: flex
就可以让文档流变为横向排列,因为flex-direction
默认是row
,所以不需要设置;
2.3 均分空间
<div class="wrap">
<h3>均分空间</h3>
<div class="flex-container">
<!-- 没有设置宽度,效果是可以根据内容进行扩展,但是不能均分 -->
<div class="flex-item" style="flex-grow: 1;">决满造着眼将东理音论</div>
<div class="flex-item" style="flex-grow: 1;">院层强西九米器</div>
<div class="flex-item" style="flex-grow: 1;">五养土</div>
<div class="flex-item" style="flex-grow: 1;">南已科声低布维步</div>
<div class="flex-item" style="flex-grow: 1;">走经育身</div>
<div class="flex-item" style="flex-grow: 1;">月之你好又从飞家</div>
<div class="flex-item" style="flex-grow: 1;">不较及用了叫资门化</div>
<div class="flex-item" style="flex-grow: 1;">他人小太从前</div>
</div>
<div class="flex-container">
<!-- 设置宽度,效果才可以均分 -->
<div class="flex-item" style="width: 100px; flex-grow: 1;">决满造着眼将东理音论</div>
<div class="flex-item" style="width: 100px; flex-grow: 1;">院层强西九米器</div>
<div class="flex-item" style="width: 100px; flex-grow: 1;">五养土</div>
<div class="flex-item" style="width: 100px; flex-grow: 1;">南已科声低布维步</div>
<div class="flex-item" style="width: 100px; flex-grow: 1;">走经育身</div>
<div class="flex-item" style="width: 100px; flex-grow: 1;">月之你好又从飞家</div>
<div class="flex-item" style="width: 100px; flex-grow: 1;">不较及用了叫资门化</div>
<div class="flex-item" style="width: 100px; flex-grow: 1;">他人小太从前</div>
</div>
<div class="flex-container">
<!-- 使用flex-basis和设置宽度的效果是相同的,不需要管值是什么,有值就行 -->
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">决满造着眼将东理音论</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">院层强西九米器</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">五养土</div>
</div>
<div class="flex-container">
<!-- 特殊情况下,英文字母是不会换行的,这样也无法均分 -->
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">sifhaiudghaiaroihefiuaiugag</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">akjdhfiuassdiuhaudf</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">aefhaeugaurguiagdjhaioghadgoiadgfhaifghjaifhgioafohagfiu</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">asdhiasdh</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">qwlkhrihaeirghuihgiahugiae</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;">sdakgaihdgihadifoghaoughjanvjhzxciuf</div>
</div>
<div class="flex-container">
<!-- 只需要设置 min-width:0 即可解决这个问题,文字溢出的问题按自己需要的方式处理即可 -->
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;min-width: 0;">sifhaiudghaiaroihefiuaiugag</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;min-width: 0;">akjdhfiuassdiuhaudf</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;min-width: 0;">aefhaeugaurguiagdjhaioghadgoiadgfhaifghjaifhgioafohagfiu</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;min-width: 0;">asdhiasdh</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;min-width: 0;">qwlkhrihaeirghuihgiahugiae</div>
<div class="flex-item" style="flex-basis: 1px; flex-grow: 1;min-width: 0;">sdakgaihdgihadifoghaoughjanvjhzxciuf</div>
</div>
</div>
有时候我们做的是一个横向排列的列表,我们希望这个列表的每一个元素都是均分的,并且能给容器撑满,这个时候我们可以使用flex-grow: 1
来实现;
但是会发现元素并没有均分,其实是因为一个属性flex-basis
的默认值是auto
,这个值是根据内容的大小来决定的,可能感觉这个属性没咋听说过,因为在Flexbox
中直接使用width\height
和使用flex-basis
的效果是相似的;
对于特殊情况,例如英文单词是不会换行的,这种情况也会造成内容不会均分,这个时候只需要设置min-width: 0
即可解决这个问题;
tips: 常用的语法糖
flex: 1
,这个属性等价于flex-grow: 1; flex-shrink: 1; flex-basis: 0%;
,通常很多同学是直接使用这种方式来实现均分空间的,但是发现内容没有均分,就是因为内容的大小决定了flex-basis
的大小,为了保险起见可以在设置了flex: 1
的情况下再追加min-width: 0
;
2.4 换行
<div class="wrap">
<h3>换行</h3>
<div class="flex-container" style="flex-wrap: wrap;">
<!-- 均分的子元素不用管对齐,但是最后一行无法保证和前面行宽度一直 -->
<div class="flex-item" style="flex-basis: 200px; flex-grow: 1;">决满造着眼将东理音论</div>
<div class="flex-item" style="flex-basis: 200px; flex-grow: 1;">院层强西九米器</div>
<div class="flex-item" style="flex-basis: 200px; flex-grow: 1;">五养土</div>
<div class="flex-item" style="flex-basis: 200px; flex-grow: 1;">南已科声低布维步</div>
<div class="flex-item" style="flex-basis: 200px; flex-grow: 1;">走经育身</div>
<div class="flex-item" style="flex-basis: 200px; flex-grow: 1;">月之你好又从飞家</div>
<div class="flex-item" style="flex-basis: 200px; flex-grow: 1;">不较及用了叫资门化</div>
<div class="flex-item" style="flex-basis: 200px; flex-grow: 1;">他人小太从前</div>
</div>
<div class="flex-container" style="flex-wrap: wrap;">
<!-- 不均分默认左对齐,但是右侧会留空白 -->
<div class="flex-item">决满造着眼将东理音论</div>
<div class="flex-item">院层强西九米器</div>
<div class="flex-item">五养土</div>
<div class="flex-item">南已科声低布维步</div>
<div class="flex-item">走经育身</div>
<div class="flex-item">月之你好又从飞家</div>
<div class="flex-item">不较及用了叫资门化</div>
<div class="flex-item">他人小太从前</div>
</div>
</div>
有时候我们希望元素可以换行,这个时候我们可以使用flex-wrap: wrap
来实现,这并不是什么需要太多技巧的事情,但是UI需求可能需要对其,需要每个元素的宽度是一致的,这种情况Flexbox
没有太好的解决方案,可以使用Grid
布局来实现;
关于对齐相关的问题,我这篇文章不会出现,因为对齐在Flexbox
中也是对剩余空间的处理,而中间空隙的宽度是多少,两侧是否留白等这些细节问题需要根据实际情况来处理,如果你希望的是元素永远都保持左对齐,那么可以可以参考张鑫旭老师的这篇文章让CSS flex布局最后一行列表左对齐的N种方法;
3. Flexbox 纵向排列
Flexbox
默认是横向排列的,如果我们希望元素纵向排列,那么可以使用flex-direction: column
来实现,这个属性的值有四个,分别是row
、row-reverse
、column
、column-reverse
,默认值是row
;
3.1 内容滚动
<div class="wrap">
<h3>锁定表头</h3>
<div class="flex-container" style="flex-direction: column; height: 400px; resize: block; overflow: hidden;">
<div class="flex-item">这里是头部,将会被固定在这里</div>
<div class="flex-item" style="flex-grow: 1; overflow: auto;">
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
</div>
</div>
<div class="flex-container" style="flex-direction: column; height: 400px; resize: block; overflow: hidden;">
<div class="flex-item">这里是头部,将会被固定在这里</div>
<div class="flex-item" style="flex-grow: 1; overflow: auto;">
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
<p>这里是内容,内容会随着滚动而滚动</p>
</div>
<div class="flex-item">还可以有底部</div>
</div>
</div>
有时候我们希望顶部固定,或者顶部底部都固定,内容发生滚动,这里的小技巧其实和2.2 内容超出滚动
是一样的,只需要将flex-container
的flex-direction
设置为column
,然后对需要滚动的内容设置flex-grow: 1
让其占据剩余空间,然后设置overflow: auto
即可;
这个技巧猛一看可能没有什么用,如果有一个表格页面,表头固定,内容撑满,底部还有一个分页器是不是非常合适?我曾经正好遇到过这样的场景,也是使用这个技巧来实现的,具体可以看我这篇文章弹性布局的一次奇妙探索,对布局的掌控力又增加一分;
tips: 固定页头的方案有很多,还可以使用
position: sticky
来固定页头,Flexbox
的优势在于简单好理解,当然也有自身的局限性,这一块就不在这里展开了;
3.2 纵向 + 横向反转
<div class="wrap">
<h3>纵向+横向反转</h3>
<div class="flex-container list-odd__reverse">
<div class="flex-item">
<div class="dot"></div>
<div>这里是内容</div>
</div>
<div class="flex-item">
<div class="dot"></div>
<div>这里是内容</div>
</div>
<div class="flex-item">
<div class="dot"></div>
<div>这里是内容</div>
</div>
<div class="flex-item">
<div class="dot"></div>
<div>这里是内容</div>
</div>
<div class="flex-item">
<div class="dot"></div>
<div>这里是内容</div>
</div>
</div>
</div>
.list-odd__reverse {
flex-direction: column;
width: 300px;
}
.list-odd__reverse .flex-item {
display: flex;
}
.list-odd__reverse .flex-item:nth-child(odd) {
flex-direction: row-reverse;
}
.dot {
width: 20px;
height: 20px;
background: #40A8C4;
border: 1px solid #f39c12;
border-radius: 50%;
}
有时候我们需要实现一个错落有致的布局,这个时候我们可以使用flex-direction: column
来实现纵向排列,然后使用flex-direction: row-reverse
来实现横向反转;
这种布局最常见的地方就是微信聊天界面
,不难发现,自己的消息是在右边,别人的消息是在左边,这种布局就可以使用这种方式来实现;
还有部分UI设计出来的时间线,时间分部在线的左右两侧,这种布局也可以使用这种方式来实现;
tips: 这种方案在处理一些特殊的布局时非常有用,例如国际化的时候,部分地区的布局是从右到左的,当然处理国际化是一个很大的话题,还有很多其他的方案,这里只是提供一种思路;
结语
距离上一次写Grid
布局的文章过去了快一年的时间了,当时那篇文章的反响很好,评论区也有很多朋友留言说希望我能写一篇关于Flexbox
布局的文章,但是自己一直迟迟没有动笔;
Flexbox
不同于Grid
,我在写Grid
的文章中,是将Grid
作为一个大的布局容器来作为文章的主体,这类容器在网上有很多可以借鉴的案例,我只需要将这些布局使用Grid
来实现一遍即可;
对于Flexbox
布局并没有那么多花活可以整,这篇文章也是我在日常开发过程中整理出来比较常用的方案和方法;
我上面列举出来的一些案例应该可以应对大多数对一维布局的需求,Flexbox
其实有非常多的属性,很多人觉得学习起来很难,但是只需要掌握我文中出现的属性,基本上Flexbox
就就能熟练的使用了;
Flexbox
的核心点就在于flex-grow
、flex-shrink
、flex-basis
这三个属性,这三个属性决定了元素的大小,剩余空间的分配,这三个属性的组合可以实现很多的布局效果;
对于我来说,Flexbox
能写的内容并不多,我希望实用价值更高一些,能写的案例又太少(因为我没有经历太多复杂的布局场景),不过到此弹性布局的两篇文章就算是告一段落了,完结撒花;