phenomLi / Blog

Comments, Thoughts, Conclusions, Ideas, and the progress.
219 stars 17 forks source link

Sticky Footer ! #17

Open phenomLi opened 6 years ago

phenomLi commented 6 years ago

平时在做项目或者造轮子的时候,经常会有这样一个需求:
当页面内容超出屏幕,页脚模块会像正常页面一样,被推到内容下方,需要拖动滚动条才能看到。而当页面内容小于屏幕高度,页脚模块会固定在屏幕底部,就像是底边距为零的固定定位。
这种特殊的布局方式,就叫固定底部(Sticky Footer)
以前我曾经碰到过类似的需求,当时情况并不允许使用flex,于是一直束手无策,所以Sticky Footer算是我一直存在的CSS的一个知识漏洞,现在掌握了,赶紧记下来。

Sticky Footer的主要实现方式

Sticky Footer的实现方式有不少,我这里就只介绍最常用的3种。
很多文章介绍Sticky Footer实现方法的时候,喜欢将flex方法放在最后一位,然而我会放在第一位,原因是他们是按照CSS属性的常用程度来排序的,我这里会按照理解难易程度来排序。

下面所有的例子都会以这样的基础HTML来实现:

<body>
    <!-- 内容 -->
    <div class="content">
        Content
    </div>
    <!-- 页脚 -->
    <div class="footer">Footer</div>
</body>


壹:FLEX

flex方法应该是最简单方便的实现方式了,只要记住理解每个flex属性,就很容易实现一个Sticky Footer。一个简单地例子:

body {
    padding: 0;
    margin: 0;
    box-sizing: border-box;

    /**
    * 核心代码
    * flex-direction: column  => 使flex容器的主轴变成纵轴
    * justify-content: space-between => 让内容在主轴中向主轴两端靠
    */
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

非常简单,只需要在body里面设置样式,没什么可说的,理解了flex就很容易懂。


貳:绝对定位

绝对定位的实现方式比flex稍微复杂了一点点:

.content {
    /**
    * 核心代码
    * padding-bottom: 50px  => 因为footer为绝对定位,
    * 为了防止content覆盖了footer,所以要给content设置一个padding-bottom,
    * 而且padding-bottom的值要等于footer的高度
    */
    padding-bottom: 50px;

    padding: 40px 0 40px 0;
    background-color: aquamarine;
    text-align: center;
}

.footer {
    /**
    * 核心代码
    * 相对于flex方法,缺点就是footer一定要设置定高
    */
    position: absolute;
    left: 0;
    bottom: 0;
    height: 50px;

    width: 100%;
    text-align: center;
    background-color: #eee;
}

要理解这种方法的关键在于要理解Content的margin-bottom的作用。
为什么要设置跟Footer一样高度的margin-bottom?因为Footer是绝对定位,当Content达到某个高度时,必定会发生Content和Footer覆盖的现象(按照CSS叠层规则,是Footer覆盖Content),所以Content一定要腾出跟Footer一样高的空间用作给Footer覆盖,看图: 这种方法比第一种flex难理解一点,个人不推荐这种方法,因为绝对定位是一种不稳定的布局。


叁:负MARGIN(最hack的方法)

很巧妙的一种方法,也是很hack的一种方法。第一次看见的时候花了很久才弄明白,之后觉得十分惊艳。先看实现方式:

html, body {
    /**
    * 核心代码
    * 设置html和body的height为100%,让body的高度等于document的高度,
    * 主要目的是为了content的min-height能生效
    */
    height: 100%;

    padding: 0;
    margin: 0;
}

.content {
    /**
    * 核心代码
    * margin-bottom: -50px  => 关键1,用负的margin-bottom让footer层叠到content,而且数值也是要和footer设置的高度一致
    * padding-bottom: 50px => 跟绝对定位的方法一样,设置padding-botom消除层叠带来的影响
    * box-sizing: border-box => 设置border-box,规范盒子模型,消除padding对height的影响
    * min-height: 100%  => 关键2,让content占满body的空间
    */
    margin-bottom: -50px;
    padding-bottom: 50px;
    box-sizing: border-box;
    min-height: 100%;

    background-color: aquamarine;
    text-align: center;
}

.footer {
    /**
    * 核心代码
    * 这种方法也需要设置footer定高
    */
    height: 50px;

    text-align: center;
    background-color: #eee;
}

为什么这种方法可以?原理又是什么,我们来图解一下: 首先,若设置了Content的min-height为100%`,那么很显然Content会占满整个浏览器可视区域,所以Footer会被挤到下面,超出浏览器可视区域,浏览器会出现滚动条,这显然不好。

为什么要使用min-height而不是height,是因为要让当Content的子元素小于浏览器可视高度时,Content可填满浏览器可视区域,而当Content的子元素大于浏览器可视高度时,Content的高度能被子元素撑开。只有min-height有这样灵活的特性。


所以,我们可以利用负margin的特性,让Footer层叠在Content上面: 为了消除层叠带来的影响,可以在Content上设置padding-bottom 基本的原理就是这样子,主要还是利用了 负margin和min-height的特性。



总结

个人建议:

在平常开发中,如果不是十分在意兼容性,推荐使用flex,如果是比较在意兼容性的,最好使用负margin的方法。至于绝对定位的方法,最好不要优先考虑。