页面布局方案小结

本篇总结了常见的页面布局解决方案,包括水平居中布局、垂直居中布局、水平垂直居中布局、双列自适应布局、多列自适应布局、多列等分布局、多列等高布局、经典的顶底单栏+中部双栏的全屏布局,并比较了各实现方案的优缺点。整理自网易前端微专业《页面架构》课程,供前端爱好者学习交流参考。

1.居中布局

布局方案的思路:了解CSS属性值的特性,分解问题(需求),选择最合理的特性将其实现。

1.1 水平居中

1.1.1 text-align + inline-block

方法:子元素设置inline-block,利用其“宽度=内容”的特点。父元素设置text-align: center实现居中。

特点:兼容性好(利用display: inline; zoom: 1;代替inline-block即可兼容IE6、7),但在父容器设置text-align: center会导致其子元素中的文字也居中,实际上很多情况下我们不希望子元素的文字居中(可以通过在child中再加一句text-align: left解决)。

1
2
3
4
5
6
7
8
9
10
11
12
13
<div class="parent">
<div class="child">Demo</div>
</div>
/* 本节其余部分的HTML结构同上,代码中不再赘述*/

<style>
.child {
display: inline-block;
}
.parent {
text-align: center;
}
</style>

1.1.2 table + margin

方法:将元素的display属性设置为table,利用table在表现上类似 block 元素、而“宽度=内容”(而不像block元素的默认宽度是撑满父元素)的特点。

特点:无需设置父元素样式。适用≥ IE 8,IE6、7虽然不支持display: table,但可以直接使用table结构。

1
2
3
4
5
6
<style>
.child {
display: table;
margin: 0 auto;
}
</style>

1.1.3 absolute + transform

方法:给父容器设置相对定位position: relative,让父容变器成绝对定位的参照物。将子元素设置为绝对定位position: absolute,以利用绝对定位元素“宽度=内容”的特点。再通过left: 50%和transform: translateX(-50%)实现居中。

特点:绝对定位脱离文档流,不会对后续元素的布局造成影响。但transform为CSS3新增的属性,有兼容性问题,即使在高版本浏览器,也可能需要加上私有前缀实现兼容。

1
2
3
4
5
6
7
8
9
10
<style>
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
</style>

1.1.4 flex + justify-content / margin

方法:给父容器设置display: flex,则容器内的子元素就自然成了flex-item,默认情况下宽度是auto(即内容宽度)。再给父容器设置justify-content: center,使其子元素居中;或给子元素设置margin: 0 auto,同样可以实现居中。

特点:flex + justify-content只需设置父节点属性。但flex同样有兼容性问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<style>
.parent {
display: flex;
justify-content: center;
}
</style>


/* 或对子元素设置margin可以达到同样效果 */
<style>
.parent {
display: flex;
}
.child {
margin: 0 auto;
}
</style>

1.2 垂直居中

1.2.1 table-cell + vertical-align

方法:将父容器的display属性设置为table-cell,使其作为一个单元格(使其可以应用vertical-align属性),再对其设置verticle-align: middle。vertical-align属性可以用在inline, inline-block, table-cell元素上。

特点:兼容性好,适用≥ IE 8,IE6、7虽然不支持display: table,但可以直接使用table结构。

1
2
3
4
5
6
<style>
.parent {
display: table-cell;
vertical-align: middle;
}
</style>

1.2.2 absolute + transform

方法:给父容器设置相对定位position: relative,让父容变器成绝对定位的参照物。将子元素设置为绝对定位position: absolute,以利用绝对定位元素“高度=内容”的特点。再通过top: 50%和transform: translateY(-50%)实现居中。

特点:绝对定位脱离文档流,不会对后续元素的布局造成影响。但transform为CSS3新增属性,有兼容性问题。即使在高版本浏览器,也可能需要加上私有前缀实现兼容。此外需要注意的是,如果绝对定位的子元素是其父元素唯一的元素,则父元素也会失去高度。

1
2
3
4
5
6
7
8
9
10
<style>
.parent {
position: relative;
}
.child {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
</style>

1.2.3 flex + align-items

方法:给父容器设置display: flex,则容器内的子元素就自然成了flex-item,默认情况下父容器的align-item属性值是stretch(于是子元素将会被拉伸到和父容器同样高)。因此将父容器的align-item值设置为center即可实现垂直居中。justify-content: center,使其子元素居中;或给子元素设置margin: 0 auto,同样可以实现居中。

特点:只需设置父节点属性,无需设置子元素。但flex、align-item有兼容性问题

1
2
3
4
5
6
<style>
.parent {
display: flex;
align-items: center;
}
</style>

1.3 水平、垂直居中

1.3.1 inline-block + text-align (水平)+ table-cell + vertical-align(垂直居中)

方法:综合1.1.1和1.2.1方法

特点:兼容性好(参考1.1.1和1.2.1中的兼容性实现方案)。

1
2
3
4
5
6
7
8
9
10
<style>
.parent {
text-align: center;
display: table-cell;
vertical-align: middle;
}
.child {
display: inline-block;
}
</style>

1.3.2 absolute + transform

方法:综合1.1.3和1.2.2方法

特点:绝对定位脱离文档流,不会对后续元素的布局造成影响。但transform为CSS3新增属性,有兼容性问题。

1
2
3
4
5
6
7
8
9
10
11
<style>
.parent {
position: relative;
}
.child {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
</style>

1.3.3 flex + justify-content + align-items

方法:综合1.1.4和1.2.3方法

特点:只需设置父节点属性,无需设置子元素。但flex、align-items等有兼容性问题。

1
2
3
4
5
6
7
<style>
.parent {
display: flex;
justify-content: center;
align-items: center;
}
</style>

2.多列布局

2.1 双列,一列定宽,一列自适应 (以右列自适应为例)

2.1.1 float + margin

方法:左栏设置float,右栏的margin-left值设为“左栏宽度+间距”

特点:容易理解,但兼容性有一点问题,IE6中会产生3像素的BUG(文字会向右缩进3像素),可以在左栏加入margin-left:-3px以解决。此外,当右栏清除浮动时,右栏会掉到与左栏的底部平齐为止。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style>
.left {
float: left;
width: 100px;
}
.right {
margin-left: 100px
}
</style>

2.1.2 float + margin + fix 优化版

方法:在2.1.1的基础上,在右栏节点“right”外再套一个“right-fix”节点,对right-fix节点设置float: right; width: 100%; margin-left: -100px;

特点:不会存在3像素缩进的BUG,且对右栏right节点清除浮动也不会存在2.1.1中的问题。但由于right-fix的z值比left高(且二者都是宽度100%),因此左栏被覆盖,不可选择,需要设置.left{position:relative}来提高left的层级。兼容性非常好,缺点是代码量较大,多一层结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right-fix">
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>
</div>

<style>
.left {
float: left;
width: 100px;
}
.right-fix {
float: right;
width: 100%;
margin-left: -100px;
}
.right {
margin-left: 100px
}
</style>

2.1.3 float + overflow

方法:左栏浮动,右栏设置overflow: hidden,以触发BFC模式(块级格式化文本),BFC模式下容器的内容与外界的元素是隔离的,外界的浮动元素是不会影响到BFC模式下的容器中的内容。栏间距设置在左栏的margin-right上。

特点:样式简单,但不支持IE6。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style>
.left {
float: left;
width: 100px;
}
.right {
overflow: hidden;
}
</style>

2.1.4 table

方法:父容器设置display: table并设宽度为100%,左右栏均设置为table-cell(单元格形式)。这利用了table的显示特性,table中同行的列宽度之和等于总宽度,因此此时设置左栏宽度后,右栏宽度将自动取值为剩下的宽度。此外,table是根据内容排版的,因此在父元素上再添加table-layout:fixed,以实现布局优先、加速渲染。table-cell无法设置margin,但可以设置padding,因此可以将栏间距设置在左栏的padding-right上。

特点:代码量大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style>
.parent {
display: table;
width: 100%;
table-layout: fixed;
}
.left {
display: table-cell;
width: 100px;
}
.right {
display: table-cell;
/*宽度为剩余宽度*/
}
</style>

2.1.5 flex

方法:在父容器上设置flex,则左右栏自动变成flex-item,默认宽度为各自的内容宽度。栏间距设在left的margin-right上。右栏设置空间分配模式flex:1(等价于1,1,0,即增长因子、收缩因子都为1,基础宽度为0,即容器的剩余宽度都被分配给了右栏)。

特点:flex为CSS3新增,存在兼容性问题。性能方面可能存在问题,只适合小范围布局。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style>
.parent {
display: flex;
}
.left {
width: 100px;
margin-left: 20px;
}
.right {
flex: 1;
}
</style>

2.2 三列,两列定宽,一列自适应(以右列自适应为例)

思路同2.1。示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="center">
<p>center<p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style>
.left, .center {
float: left;
width: 100px;
margin-right: 20px;
}
.right {
overflow: hidden;
}
</style>

2.3 存在不定宽列(由内容决定宽度)的情况

需求:双列,一列由内容决定宽度,一列自适应;或多列,一列由内容决定宽度,一列自适应。

方法:

1. float + margin和float + margin + fix (2.1.1和2.1.2):由于左栏width和右栏margin-left是强耦合关系,必须同时修改,因此无法用于本需求。

2. float + overflow (2.1.3):右栏宽度与左栏无耦合,可以很好地实现需求,且代码量小,但在IE6中存在兼容性问题。

3. table:右栏宽度取左栏的剩余宽度,因此无耦合。但要实现左栏宽度随内容变化,需要去掉父容器上的table-layout:fixed,并给左栏设置一个非常小的宽度width: 0.1%(之所以不用1px是因为在IE8中会出现问题),再给.left p设置一个width值将左栏宽度撑开。display在IE6、7中存在兼容性问题。

4. flex:同样无耦合,去掉left中的width即可实现由内容决定宽度。存在兼容性问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="center">
<p>center</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style type="text/css">
.left,.center{
float: left;
margin-right: 20px;
}
.right{
overflow: hidden;
}
.left p,.center p{
width: 100px;
}
</style>

2.4 多列等分

每一列的宽度、列间间隙均相等,下面为多列等分布局的布局特定。

父容器宽度为c,每列宽为w,列间间隙为g,列数为n,则有:

c=wn+g(n-1)

更常用的形式为c+g=n(w+g)

可见,实际上需要的总宽要给c再加上一个g

2.4.1 float

方法:

(1)总宽调整:给父容器总宽度加上一个间距(本节g=20px为例),在父容器设置margin-left: -20px;

(2)列宽调整:让列元素宽度包含间距,在.column设置padding-left:20px;和box-sizing: border-box;

(3)等分:在.column设置width:25%;

特点:完美兼容IE8,IE6、7对浮点四舍五入的处理导致可能存在问题。此外,这一方法的结构和样式具有耦合性(例如,4列改5列时要将.column的25%修改为20%)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="parent">
<div class="column"><p>1</p></div>
<div class="column"><p>2</p></div>
<div class="column"><p>3</p></div>
<div class="column"><p>4</p></div>
</div>
<style media="screen">
.parent {
margin-left: -20px;
}
.column {
float: left;
width: 25%;
padding-left: 20px;
box-sizing: border-box;
}
</style>

2.4.2 table

方法:

(1)总宽调整:首先,为了解决设置成width:100%的父容器无法通过margin增加宽度,在parent节点外套一个parent-fix节点。这时再给parent-fix的设置margin-left: -20px;,将总宽度的增加转嫁到parent的上一层。

(2)列宽调整:让列元素宽度包含间距,在形式为table-cell的.column上设置padding-left:20px即可。

(3)等分:在.parent设置table-layout: fixed,除了布局优先、加速渲染的功能外,还具有自动平分各列单元格的特性。

特点:结构与样式解耦合,但多一层结构,代码量较大。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class='parent-fix'>
<div class="parent">
<div class="column"><p>1</p></div>
<div class="column"><p>2</p></div>
<div class="column"><p>3</p></div>
<div class="column"><p>4</p></div>
</div>
</div>

<style media="screen">
.parent-fix {
margin-left: -20px;
}
.parent {
display: table;
width: 100%;
table-layout: fixed;
}
.column {
display: table-cell;
padding-left: 20px;
}
</style>

2.4.3 flex

方法:父容器设置flex,列元素设置flex:1(即1,1,0)实现剩余空间的等分。通过.column+.column选择器选中除第一列外的所有列,并设置margin-left以加上列间距。

特点:代码量很少,结构与样式无耦合,但存在兼容性问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<div class="parent">
<div class="column"><p>1</p></div>
<div class="column"><p>2</p></div>
<div class="column"><p>3</p></div>
<div class="column"><p>4</p></div>
</div>

<style media="screen">
.parent {
display: flex;
}
.column {
flex: 1;
}
.column+.column {
margin-left: 20px;
}
</style>

2.5 等高布局

2.5.1 table

方法:table具有“每列等宽、每行等高”的天然等高特性。
但需要注意,默认情况下,左栏背景颜色会显示在其padding-right的区域,造成看上去间距失效的效果,因此要记得给.left设置background-clip: content-box;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style>
.parent {
display: table;
width: 100%;
table-layout: fixed;
}
.left {
display: table-cell;
width: 100px;
}
.right {
display: table-cell;
}
</style>

2.5.2 flex

方法:flex的align-item默认值是stretch,因此同样存在天然的等高效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style>
.parent {
display: flex;
}
.left {
width: 100px;
margin-left: 20px;
}
.right {
flex: 1;
}
</style>

2.5.3 float

方法:float没有任何特性做到两列等高,但给两列都增加一个很大的padding-bottom值(如9999px)和一个很大的负数margin-bottom值(-9999px),并给父容器加上overflow:hidden,即可实现将两列背景色按最高的一列截取的效果。

特点:实际上是伪等高(元素真实高度不等,只是背景看起来高度相等),兼容性相比2.5.1和2.5.2来说较好。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<div class="parent">
<div class="left">
<p>left</p>
</div>
<div class="right">
<p>right</p>
<p>right</p>
</div>
</div>

<style>
.parent {
overflow: hidden;
}
.left,
.right {
padding-bottom: 9999px;
margin-bottom: -9999px;
}
.left {
float: left;
width: 100px;
margin-right: 20px;
}
.right {
overflow: hidden;
}
</style>

3.全屏布局

需求:顶部、底部单栏,中部分为左右双栏(右栏为主内容区),整体全屏自适应。常见于公司管理系统、监控统计平台等场景。

3.1 顶栏底栏定高,中部左栏定宽,其余宽高自适应

定高和定宽可以由绝对px值确定,也可以用百分比确定,本节以前者为例,后者只需把将px值改为百分比即可。

3.1.1 常规position方案

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<div class="parent">
<div class="top"></div>
<div class="left"></div>
<div class="right">
<div class="right_content"></div> <!-- right中嵌套的right_content是用于实现内容区滚动的辅助结构 -->
</div>
<div class="bottom"></div>
</div>

<style>
html,body,.parent {
height: 100%; /*overflow用于隐藏滚动条*/
overflow: hidden;
}
/*绝对定位参照物是body*/
.top {position: absolute;top: 0;left: 0;right: 0;height: 100px;}
.left {position: absolute;left: 0;top: 100px;bottom: 50px;width: 200px;}
/*给right设置overflow: auto,由于right中嵌套了比其高度更高的right_content,因此可以实现滚动效果*/
.right {position: absolute;left: 200px;right: 0;top: 100px;bottom: 50px;overflow: auto;}
/*给right_content设置一个很高的高度*/
.right .right_content {min-height: 1000px;}
.bottom {position: absolute;left: 0;right: 0;bottom: 0;height: 50px;}
</style>

其IE6兼容版可以通过Hack方案实现,有兴趣可做了解。

3.1.2 CSS3新方案:flex

CSS3中的新概念所有IE9及其也行版本都不兼容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<div class="parent">
<div class="top"></div>
<!-- 与3.1.1不同之处,在left和right外套了一层middle -->
<div class="middle">
<div class="left"></div>
<div class="right">
<div class="right_content"></div>
</div>
</div>
<div class="bottom"></div>
</div>

<style media="screen">
html,body,.parent {height: 100%;overflow: hidden;}
/*parent设置flex-direction为column,沿竖向布局(因为top,middle,bottom由上向下排列)(此属性默认为row)*/
.parent {display: flex;flex-direction: column;}
.top {height: 100px;}
.bottom {height: 50px;}
/*middle设置flex:1,占据top和bottom以外剩余的全部高度;middle本身也作为一个flex布局容器*/
.middle {flex: 1;display: flex;}
.left {width: 200px;}
/*right设置flex:1,占据left以外全部剩余宽度。设置overflow: auto使内容区能实现滚动*/
.right {flex: 1;overflow: auto;}
/*right_content设置的用意同3.1.1一致*/
.right .right_content {min-height: 1000px;}
</style>

3.2 顶栏底栏高度、中部左栏宽度由内容决定,其余宽高自适应

此时Postion的定位方法不适合。

3.2.1 Flex

只要删除确定的宽高属性值,即可实现区域的宽(高)根据内容自适应。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<div class="parent">
<div class="top"></div>
<div class="middle">
<div class="left"></div>
<div class="right">
<div class="right_content"></div>
</div>
</div>
<div class="bottom"></div>
</div>

<style media="screen">
/*删除.top、.bottom的height属性定义,删除.left的width属性定义,即可实现由内容决定宽高*/
html,body,parent {height: 100%;overflow: hidden;}
.parent {display: flex;flex-direction: column;}
.middle {flex: 1;display: flex;}
.right {flex: 1;overflow: auto;}
.right .right_content {min-height: 1000px;}
</style>

3.2.2 Grid方法

W3C 草案并不稳定,浏览器支持也并不理想,有兴趣可做了解。

3.3 方法比较

方案 兼容性 性能 自适应
Position 部分自适应
Flex 较差 可自适应
Grid 较好 可自适应

本站所刊载图文之著作权归本站作者Qinsman(qinsman.com)所有,欢迎分享、转发,如需转载,请联系作者(jiaqi_dong@163.com)并标明出处及原链接。非经本站授权许可禁止转载,未经授权许可的转载均视为商业用途,请按20元/字之标准,于转载后15日内联系本站付款。转载人的转载行为,视为同意本条。转载须附本条。

Qinsman wechat
关注我的公众号,一个卖馒头,也卖故事的地方:)