一直以来,简单、高效、精准且兼容性强的布局能力都是css
努力的方向,但在传统布局中,只有float
和position
布局是可靠且跨浏览器兼容的技术。为进一步提高布局能力,css
在2009年推出布局利器flex
和后面推出的布局王者grid
,其中,flex
拥有强大的一维布局能力,而grid
则拥有更强大的二维布局能力。今天,我们主要基于居中问题、空间分布问题,margin+flex
布局和响应式布局讨论布局利器flex
技术。
居中问题
传统布局技术中,水平垂直居需求可以针对不同的元素类型使用text-align
、margin
、padding
、position
等手段实现,但是比较麻烦的这些技术使用都有特定对象,比如text-align
和line-height
针对的是内联元素,而margin: auto
针对的是块级元素。flex
布局技术中,简化了应用场景,无论是什么类型的元素,只要简单的设置just-content: center
就可实现水平居中,简单设置align-items
就可实现垂直居中,两者一并设置就可实现水平垂直居中。我们来看一下几个例子:
首先块级元素可以很容易的实现水平垂直居中。
<head>
<style>
.father {
/* 容器中设置flex布局 */
display: flex;
/* 实现水平居中 */
justify-content: center;
/* 实现垂直居中 */
align-items: center;
width: 500px;
height: 500px;
background-color: antiquewhite;
}
.son {
width: 100px;
height: 100px;
background-color: lightpink;
}
</style>
</head>
<body>
<div class="father">
<div class="son"></div>
</div>
</body>
复制代码
然后,我们来看看内联元素,同一套代码实现水平垂直居中也是没问题的。
<head>
<style>
.father {
/* 容器中设置flex布局 */
display: flex;
/* 实现水平居中 */
justify-content: center;
/* 实现垂直居中 */
align-items: center;
width: 500px;
height: 500px;
background-color: antiquewhite;
}
.son {
width: 100px;
height: 100px;
background-color: lightpink;
}
</style>
</head>
<body>
<div class="father">
<!-- 将子元素直接替换为内联元素 -->
<span class="son"></span>
</div>
</body>
复制代码
通常,内联元素无法通过设置width
和height
属性来确定元素的大小,但是一旦设置了flex
布局,则自动给内联元素添加了display: block
,而且flex
布局会让float
、clear
、vertical-align
属性失效。
值得我们注意的是,flex
布局的影响仅仅只是针对容器的直接子元素,而非所有的后代。我们看看下面的例子
<head>
<style>
.father {
display: flex;
justify-content: center;
align-items: center;
width: 500px;
height: 500px;
background-color: antiquewhite;
}
.container {
width: 200px;
height: 200px;
background-color: rgb(9, 131, 238);
}
.son {
width: 100px;
height: 100px;
background-color: lightpink;
}
</style>
</head>
<body>
<div class="father">
<!-- 添加一个容器包裹son -->
<div class="container">
<span class="son">son</span>
</div>
</div>
</body>
复制代码
通过添加一层容器包裹住span
元素,则无法将flex
的影响传递至span
元素,无法设置高宽,并且水平垂直设置无法通过container
传递至非father
的直接子元素span
上。
空间分布问题
flex
布局可以克服传统布局很难实现灵活空间分布和对齐的问题。在flex
的主轴上(默认为水平x轴),可以通过设置flex
和justify-content
操控空间分配和对齐,在flex
的交叉轴上(默认为垂直y轴),可以通过设置align-items
和align-self
操控空间分配和对齐。
1.主轴空间
首先,来看看在多列布局,设置各列固定宽度,均匀分布在水平轴上,实现如下效果:
传统布局要实现各列布局自适应父容器的水平空间均匀分布是很麻烦的,至少无法直接通过堆积float+margin
实现。但是使用flex
布局可以直接设置justify-content:space-evenly
实现。
<head>
<style>
.container {
display: flex;
width: 800px;
height: 500px;
background-color: antiquewhite;
/* 设置justify-content可以均分空间 */
justify-content: space-evenly;
}
.container > * {
line-height: 50px;
text-align: center;
width: 50px;
height: 50px;
background-color: lightcoral;
}
</style>
</head>
<body>
<div class="container">
<div class="item1">item1</div>
<div class="item2">item2</div>
<div class="item3">item3</div>
<div class="item4">item4</div>
<div class="item5">item5</div>
</div>
</body>
复制代码
再来看看元素空间分配,我们可以设置flex
设置元素在主轴的空间分配,flex
属性具体空间分配算法可以参考我的另外一篇文章Flex布局空间分配。假设要设置3列空间,两边为固定宽度的sidebar
,中间为自适应宽度的content
,效果如下:
传统布局可以借助float
或者position
布局策略实现,但是都不够简洁,使用flex
布局时,只要设置中间content
区域为flex:1
(主要是调节flex-grow
属性),左右两边区域为定宽即可实现。
<head>
<style>
.main {
display: flex;
height: 500px;
background-color: antiquewhite;
}
.main > * {
line-height: 50px;
text-align: center;
}
.sidebar-left {
width: 100px;
background-color: aqua;
}
.content {
/* flex默认为0 1 auto;不具有只适应能力 */
flex:1;
background-color:coral;
}
.sidebar-right {
width: 100px;
background-color: aqua;
}
</style>
</head>
<body>
<div class="main">
<div class="sidebar-left">sidebar-left</div>
<div class="content">content</div>
<div class="sidebar-right">sidebar-right</div>
</div>
</body>
复制代码
bootstrap
的布局的栅格系统Grid system
也是利用flex
布局,设置flex
属性和width
属性将主轴空间分为12份。在bootstrap
的源码,首先row
类中设置flex
布局,然后在col-*
、col-md-*
等布局属性中设置对应占比width
以及flex
属性实现不同的空间分配。
.row {
--bs-gutter-x: 1.5rem;
--bs-gutter-y: 0;
/* 在row类中设置flex布局 */
display: flex;
flex-wrap: wrap;
margin-top: calc(-1 * var(--bs-gutter-y));
margin-right: calc(-0.5 * var(--bs-gutter-x));
margin-left: calc(-0.5 * var(--bs-gutter-x));
}
.col {
/* 在col类中设置flex为1,即初始宽度为0,所有列等分空间 */
flex: 1 0 0%;
}
.col-1 {
flex: 0 0 auto;
/* 在col-1类中设置宽度为1/12 */
width: 8.33333333%;
}
.col-2 {
flex: 0 0 auto;
/* 在col-1类中设置宽度为2/12 */
width: 16.66666667%;
}
.col-3 {
flex: 0 0 auto;
/* 在col-1类中设置宽度为3/12 */
width: 25%;
}
/* col-*后面就不写了 */
复制代码
于是,可以实现以下效果:
<div class="container">
<div class="row">
<div class="col">
1 of 3
</div>
<div class="col-6">
2 of 3 (wider)
</div>
<div class="col">
3 of 3
</div>
</div>
<div class="row">
<div class="col">
1 of 3
</div>
<div class="col-5">
2 of 3 (wider)
</div>
<div class="col">
3 of 3
</div>
</div>
</div>
复制代码
<div class="container">
<div class="row">
<div class="col">
1 of 2
</div>
<div class="col">
2 of 2
</div>
</div>
<div class="row">
<div class="col">
1 of 3
</div>
<div class="col">
2 of 3
</div>
<div class="col">
3 of 3
</div>
</div>
</div>
复制代码
最后,我们还可以利用flex
实现sticky footer
效果,如下图所示,可以将footer
直接推至页面底部,这种方案就很简洁。
<head>
<style>
html {
height: 100%;
}
body {
min-height: 100%;
display: flex;
/* 将主轴方向设置为y轴*/
flex-direction: column;
}
header {
height: 50px;
background-color: aqua;
}
main {
/* 这里将main在主轴方向上占用所有剩余空间*/
flex: 1;
background-color: aquamarine;
}
footer {
background-color: antiquewhite;
height: 50px;
}
</style>
</head>
<body>
<header>header</header>
<main>main</main>
<footer>footer</footer>
</body>
复制代码
2.交叉轴空间
在交叉轴上,我们使用flex
布局的align-items
和align-self
可以克服等高布局、底线对齐等问题,相当灵活,很是方便!
比如在第一小节居中问题上,我们利用align-items: center
设置元素为垂直居中。比如在上一小节中的3列空间只适应布局,我们利用align-items
的默认值stretch
,实现sidebar-left
、content
、sidebar-right
三列空间等高布局。
对于底线对齐布局,我们也可以简洁的利用align-items: flex-end
实现。
<head>
<style>
.container {
display: flex;
height: 500px;
width: 500px;
background-color: antiquewhite;
align-items: flex-end;
}
.container > * {
text-align: center;
width: 100px;
margin: 0 30px;
background-color: aquamarine;
}
.item1 {
height: 100px;
}
.item2 {
height: 200px;
}
.item3 {
height: 300px;
}
</style>
</head>
<body>
<div class="container">
<div class="item1">item1</div>
<div class="item2">item2</div>
<div class="item3">item3</div>
</div>
</body>
复制代码
margin+flex布局
如果说flex
布局是利器的话,那margin: auto
配合上flex
布局那就是神器。
虽然flex
布局很强,但是flex
布局的空间分配策略主要集中在flex
元素上,若是要控制元素彼此间的间距,那就弱很多,只有justify-content
属性的几个关键字可以控制。比如实现以下两端导航栏就显得比较困难了。
这种情况当然也可以用float
布局解决,也可以使用通过容器将前三个元素HOME,BLOG,ARTICLE
包裹起来,然后在用justify-content: between
实现。这里,我们使用margin:auto
将更加高效。我们可以在最后一个元素中使用margin-left: auto
。将最后一个元素推至最右边即可。
<head>
<style>
ul {
border: 1px solid rgb(216, 216, 216);
width: 80%;
margin: auto;
padding: 0;
display: flex;
}
li {
margin: 0;
list-style: none;
line-height: 3em;
border-bottom: 1px solid transparent;
padding: 0 1em;
}
li:hover {
border-bottom: 1px solid rgb(8, 250, 0);
cursor: pointer;
}
li a {
text-decoration: none;
color: black;
}
li:last-child {
/* 最后一个元素使用margin-left: auto。将自身推至最右边 */
margin-left: auto;
}
</style>
</head>
<body>
<nav>
<ul>
<li><a href="#">HOME</a></li>
<li><a href="#">BLOG</a></li>
<li><a href="#">ARTICLE</a></li>
<li><a href="#">CONTACT</a></li>
</ul>
</nav>
</body>
复制代码
如张鑫旭老师所言,margin:auto
填充规则如下:
- 如果一侧定值,一侧
auto
,则auto
为剩余空间大小 - 如果两侧均为
auto
,则平分剩余空间
我们在块级元素利用定宽+margin:auto
实现水平居中,在绝对定位中设置定宽高且对立属性为0,然后利用margin:auto
实现水平垂直居中也是利用上述的特性。而在flex
布局中,除元素本身高宽外,其他空间都是剩余空间,即可利用margin:auto
控制如何分配。
比如我们在flex
布局中设置元素定宽,然后使用margin:auto
实现水平居中。在flex
布局中设置元素定高,然后使用margin:auto
实现垂直居中,有兴趣的同学可以自行尝试。
响应式布局
借助flex
布局技术,配合媒体查询技术可以很容易的实现响应式布局,就拿上述的导航栏例子说,我们添加下面的媒体查询代码:
@media screen and (max-width:500px) {
/* 自适应导航栏 */
ul {
flex-flow: column;
}
li:last-child {
margin-left: 0;
}
}
复制代码
即可实现在屏幕宽度低于500px时,导航栏垂直布局的效果:
总结
总的来说,flex
在一维空间的控制能力是很强的,主要体现在主轴和交叉轴对齐和空间分配上操作十分简洁高效,可以很方便的实现水平垂直居中、空间等分、底部对齐、等高对齐,自适应多列布局和响应式布局,同时,margin: auto
方案配合上flex
布局更是如虎添翼,在空间分布的操控能力更上一层楼,基本可以满足日常布局需求了。当下,大多数浏览器都支持flex
布局技术了,诸如 Firefox, Chrome, Opera, Microsoft Edge 和 IE 11,若flex
满足项目兼容性需求,flex
布局是一维布局的首选方案。