一步一步学布局(sticky-footer)

粘性的页脚(sticky footer)是布局中常见的一种,简单说就是让底部区域永远留在最底部:

  1. 如果内容高度较小,底部区域固定在窗口底部
  2. 如果内容高度较大,底部区域在整体内容的最底部

img

分析阶段

考虑第一个情况

要实现底部区域固定在窗口底部,我们应该能很快想到如下解决方案:

  1. 绝对定位/固定定位(bottom:0)
  2. flex(中间部分设定 flex:1,底部设置固定高度)
  3. 对 footer 使用负 margin

考虑第二个情况

我们期望,中间内容区域高度增加时,能够将 footer 顶下去,这样的话:

  1. 绝对定位/固定定位已经脱离文档流,似乎无法解决问题
  2. flex 当当前屏幕内容(content+footer)超过一屏时,是否能做到?
  3. 对 footer 使用负 margin 似乎也能解决

逐步实现

flex 尝试阶段

如果不考虑拥抱 IE9,建议使用 flex 做布局

满足第一种

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
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: column;
}
section {
flex: 1;
}
footer {
height: 50px;
}
</style>
</head>
<body>
<section>Content</section>
<footer>Footer</footer>
</body>
</html>

上述代码设定后,满足第一种情况,此时会发现,对 section 设置高度将无效

制造第二种,填充内容

此布局 section 无法设置高度,我们对 DOM 进行调整,并且追加 content 样式,使内容超一屏

1
2
3
4
5
6
<body>
<section>
<div style="height: 1000px;">Content</div>
</section>
<footer>Footer</footer>
</body>

缺陷调整

这时候我们发现 flex 方案能满足需求,但是此时底部 Footer 高度不再是 50px,而是由子元素决定,再次对 DOM 进行调整,最终代码如下

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
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
flex-direction: column;
}
section {
flex: 1;
}
</style>
</head>
<body>
<section>
<div style="height: 1000px;">Content</div>
</section>
<footer>
<div style="height: 50px;">Footer</div>
</footer>
</body>
</html>

继续思考

如果这个布局增加头部 header,是否能适配呢?答案是肯定的,只需要调整 DOM 至如下,自己可以尝试一下:

1
2
3
4
5
6
7
8
9
10
11
<body>
<header style="height: 50px;">
<div style="height: 50px;">Header</div>
</header>
<section>
<div style="height: 1000px;">Content</div>
</section>
<footer>
<div style="height: 50px;">Footer</div>
</footer>
</body>

Tips: 当页面内容超过一屏时,此时无法直接设置 header、footer、section 高度,需要通过子元素来撑开

除了 flex 之外的方法

除了 flex,我们可以使用通配的 margin 来解决

满足第一种情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
section {
min-height: 100%;
}
footer {
margin-top: -50px;
height: 50px;
}
</style>
</head>
<body>
<section>Content</section>
<footer>Footer</footer>
</body>
</html>

制造第二种,填充内容

我们仍然将 section 内的内容撑开一屏,发现也可以完美适配

1
2
3
4
5
6
<body>
<section>
<div style="height: 1000px;">Content</div>
</section>
<footer>Footer</footer>
</body>

缺陷调整

但是,有一点瑕疵似乎需要注意到,content、footer 似乎底部内容重叠了,我们需要避免这点:

  1. 我们可以在内容区给一个 padding-bottom

    1
    2
    3
    4
    5
    6
    <body>
    <section>
    <div style="height: 1000px; padding-bottom: 50px;">Content</div>
    </section>
    <footer>Footer</footer>
    </body>
  2. 对 section 增加伪类来处理,完整代码如下:

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
29
30
31
32
33
34
<!DOCTYPE html>
<html>
<head>
<style>
html, body {
height: 100%;
margin: 0;
padding: 0;
}
section {
min-height: 100%;
position: relative;
}
section:after {
content: "";
position: relative;
display: block;
left: 0;
bottom: 0;
height: 50px;
}
footer {
margin-top: -50px;
height: 50px;
}
</style>
</head>
<body>
<section>
<div style="height: 1000px;">Content</div>
</section>
<footer>Footer</footer>
</body>
</html>

继续思考

这种方法给定了内容区域 content 高度 100%,所以如果要增加头部的话,需要采用类似方法调整:

  1. 内容区域需要设置 padding-top: 50px;
  2. header 需要设置 margin-bottom: -50px;

后记

如果有建议,可以提出来一起探讨,祝各位生活愉快~