Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[译] [706] [#41] 紧贴底部的页脚 #18

Closed
cssmagic opened this issue Nov 2, 2015 · 7 comments
Closed

[译] [706] [#41] 紧贴底部的页脚 #18

cssmagic opened this issue Nov 2, 2015 · 7 comments

Comments

@cssmagic
Copy link
Owner

cssmagic commented Nov 2, 2015


本文是早期译版,未经校审。仅供参考。


Sticky footers

紧贴底部的页脚

Prerequisites

  • Viewport-relative units (see the {#secret-xxx})
  • calc()

背景知识

  • 视口相关的长度单位(参见第 {$page$} 页的 “{$secret$}” 攻略)
  • calc()

The problem

难题

This is one of the oldest and most common problems in web design, so common that most of us have experienced it at one point or another. It can be summarized as follows: a footer with any block-level styling, such as a background or shadow, works fine when the content is sufficiently long, but breaks on shorter pages (such as error messages). The breakage in this case being that the footer does not “stick” at the bottom of the viewport like we would want it to, but at the bottom of the content.

在网页设计中,存在一个相当古老但又相当常见的问题,它是前端工程师绕不开的坎,躲得了初一逃不过十五。这个问题可以简单地概括如下:有一个具有块级样式的页脚(比如它设置了背景或阴影),当页面内容足够长时,这个页脚一切正常;而当页面较短时(比如错误信息页面)就会出现问题。此时的问题在于,页脚不能像我们所期望的那样 “紧贴” 在视口的最底部,而是紧跟在内容的下方。

{原书注释!}

Specifically, the issue appears on pages whose content is shorter than the viewport height minus the footer height.

具体来说,当页面内容的长度小于视口高度减去页脚高度时,这个问题就会出现。

It is not only its ubiquity that made it popular, but also how deceptively easy it looks at first. It’s a textbook case of the type of problem that requires significantly more time to solve than expected. In addition, this is still not a solved problem in CSS 2.1: almost all classic solutions require a fixed height for the footer, which is flimsy and rarely feasible. Furthermore, all of them are overly complicated, hacky, and have specific markup requirements. Back then, this was the best we could do, given the limitations of CSS 2.1. But can we do better with modern CSS, and if so, how?

这个问题不仅普遍存在,而且乍看起来确实相当简单。因此,在所有 “隐蔽大坑” 式的难题中,它简直就是教科书般的典型案例。不仅如此,CSS 2.1 基本上拿它没有办法:几乎所有的经典解决方案都需要给页脚设置固定的高度,而这显然是不健壮的,也是不实际的。此外,所有这些解决方案都太过复杂了太像 hack 了,而且往往还要求网页按照特定的结构来写。在那个年代,受制于 CSS 2.1 有限的能力范围,这已是我们所能达到的最好结果了。不过,在现代 CSS 协助下,我们可以做得更好吗?如果答案是肯定的,又该如何去做?

{原书注释!}

If you’ve never had the pleasure of pulling your hair out and diving in the existing literature for this problem, here are a few popular links with existing, widely used solutions that have served many a web developer before CSS Level 3 specs were conceived:

如果你从来没有在这个大坑里体验过疯魔的快感,那不妨来感受一下前人针对这个问题所积累下来的宝贵财富。在 CSS 第三版规范推出之前,这些解决方案曾经挽救过一个又一个的网页开发者:

The last two are the most minimal in the lot, but still have their own limitations.

最后两个解决方案是这众多链接中使用最少的,但仍然有其独到之处。

Fixed height solution

固定高度的解决方案

We will work with an extremely bare-bones page with the following markup inside the <body> element:

我们手头的这个页面极其简单,<body> 元素内的结构代码如下所示:

<header>
    <h1>Site name</h1>
</header>
<main>
    <p>Bacon Ipsum dolor sit amet...
    <!-- Filler text from baconipsum.com -->
    <!-- 从 baconipsum.com 那里复制了一些示意文字过来 --></p>
</main>
<footer>
    <p>© 2015 No rights reserved.</p>
    <p>Made with ♥ by an anonymous pastafarian.</p>
</footer>
图 7.23

FIGURE

How our simple page looks when its content is sufficiently long

这是一个简单的页面,当它的内容足够长时看起来是这样的。

We have also applied some basic styling to it, including a background on the footer. You can see how it looks in Figure XX.XX. Now, let’s reduce the content a bit. You can see what happens then, in Figure XX.XX. This is the sticky footer problem in all its glory! Great, we have recreated the problem, but how do we solve it?

然后我们给页面加上一些基本样式,再给页脚加上一层背景。你可以在 图 7.23 中看到它的样子。现在,让我们把页面内容缩短一点儿,然后可以在 图 7.24 中看到结果。此时页脚沉不到底的问题就完全暴露出来了!好吧,我们重现了这个问题,但我们该如何解决这个问题呢?

图 7.24

FIGURE

The sticky footer problem in all its glory

页脚沉不到底的问题相当刺眼啊。

If we assume that our footer text will never wrap, we can deduce a CSS length for its height:

假设这个页脚的文本永远不可能折行,那我们就可以推算出它实际所占的高度:

2 lines × line height + 3 × paragraph margin + vertical padding =

{-:-}2 行 × 行高 + 3 × 段落的垂直外边距 + 页脚的垂直内边距 =

{-:-}2 × 1.5em + 3 × 1em + 1em = 7em

Similarly, the header height is 2.5em. Therefore, by using viewport-relative units and calc(), we can “stick” our footer to the bottom with essentially one line of CSS:

同样,我们可以得出页头的高度是 2.5em。接下来,借助视口相关的长度单位以及 calc() 函数,我们只需一行 CSS 代码就可以表达出这种尺寸关系,从而把页脚 “固定” 到底部:

main {
    min-height: calc(100vh - 2.5em - 7em);
    /* Avoid padding/borders screwing up our height: */
    /* 避免内边距或边框搞乱高度的计算: */
    box-sizing: border-box;
}

{警告!!}

Be careful when using calc() with subtraction or addition: the + and - operators require spaces around them. This very odd decision was made for future compatibility. If at some point keywords are allowed in calc(), the CSS parser needs to be able to distinguish between a hyphen in a keyword and a minus operator.

calc() 中使用加减法时要特别当心:记得在 +- 运算符的左右各加一个空格。这条怪异的规则是为了向前兼容。在未来的某个时候,calc() 内部可能会允许使用关键字,那么 CSS 解析器就需要有依据来区分关键字中的连字符和减号运算符。

Alternatively, we could apply a wrapper around our <header> and <main> elements so that we only need to calculate the footer height:

或者换个方式,我们可以把 <header><main> 元素包在一个容器里,然后在算式中就只需要考虑页脚的高度了:

#wrapper {
    min-height: calc(100vh - 7em);
}
图 7.25

FIGURE

The footer after we’ve applied CSS to make it stick

我们运用 CSS 的计算功能将页脚固定到了底部。

This works (Figure XX.XX) and it seems to be slightly better than the existing fixed height solutions, mainly due to its minimalism. However, except for very simple layouts, this is not practical at all. It requires us to assume that the footer text will never wrap, we need to edit the min-height every time we change the footer metrics (i.e., it is not DRY), and unless we’re willing to add a wrapper HTML element around our header and content, we need to do the same calculations and modifications for the header as well. Surely, in this day and age we can do better, right?

这个方法是有效的(图 7.25),而且它似乎比已有的那些需要固定高度的方案还要稍稍好一些,主要好在它的代码极其精简。不过,如果页面布局不是这么简单的话,那这个方法就完全不实用了。它不仅要求我们确保页脚内的文本是永远不会折行的,而且每当我们改变了页脚的尺寸时,都需要跟着调整 min-height 值(也就是说,这不够 DRY);此外,除非我们愿意给页头和内容主体套一层额外的 HTML 元素,不然我们还得跟着页头的尺寸改。想必在这个美好的新时代,我们还有更好的办法,对吗?

{试一试} play.csssecrets.io/sticky-footer-fixed

Flexible solution

更灵活的解决方案

Flexbox is perfect for these kinds of problems. We can achieve perfect flexibility with only a few lines of CSS and there is no need for weird calculations or extra HTML elements. First, we need to apply display: flex to the <body> element, as it’s the parent of all three of our main blocks, to toggle Flexible Box Layout (Flexbox) for all three of them. We also need to set flex-flow to column, otherwise they will be all laid out horizontally on a single row (Figure XX.XX):

Flexbox 对于此类问题同样是完美的选择。我们只需寥寥几行 CSS 代码就可以完美达成十分灵活的布局效果,而且完全不需要复杂的计算,或是添加多余的 HTML 元素等等。首先,我们需要对 <body> 元素设置 display: flex,因为它是这三个主要区块的父元素,当父元素获得这个属性之后,就可以对其子元素触发 “伸缩盒布局模型”。我们还需要把 flex-flow 设置为 column,否则子元素会被水平排放在一行上(图 7.26):

body {
    display: flex;
    flex-flow: column;
}
图 7.26

FIGURE

Applying flex without applying anything else arranges the children of our element horizontally

只应用 flex 而没有应用其他属性时,会让所有子元素水平排列。

At this point, our page looks about the same as it did before all the Flexbox stuff, as every element occupies the entire width of the viewport and its size is determined by its contents. Ergo, we haven’t really taken advantage of Flexbox yet.

此时,页面看起来跟没有启用 Flexbox 的情况似乎是一样的,因为所有元素都占据了整个视口的宽度,而它们的高度也都是由它们自身的内容来决定的。也就是说,我们还没有真正发挥 Flexbox 的威力呢。

To make the magic happen, we need to specify a min-height of 100vh on <body>, so that it occupies at least the entire height of the viewport. At this point, the layout still looks exactly like Figure XX.XX, because even though we have specified a minimum height for the entire body element, the heights of each box are still determined by their contents (i.e., they are intrinsically determined, in CSS spec parlance).

为了完全释放它的魔力,我们首先要把 <body>min-height 属性指定为 100vh,这样它就至少会占据整个视口的高度。不过,现在整个页面的布局仍然跟 图 7.24 无异,原因在于,虽然我们已经为整个 body 元素指定了最小高度,但各个子元素的高度仍然是以各自的内容为准的(按照 CSS 规范的说法,它们的高度仍然是由内部因素来决定的)。

What we need here is for the height of the header and footer to be intrinsically determined, but the height of the content should flexibly stretch to all the leftover space. We can do that by applying a flex value that is larger than 0 (1 will work) to the <main> container:

此时我们所期望的是,页头和页脚的高度由其内部因素来决定,而内容区块的高度应该可以自动伸展并占满所有的可用空间。我们只要给 <main> 这个容器的 flex 属性指定一个大于 0 的值(比如 1 即可),就可以实现这个效果了:

body {
    display: flex;
    flex-flow: column;
    min-height: 100vh;
}

main { flex: 1; }

{小提示}

The flex property is actually a shorthand of flex-grow, flex-shrink, and flex-basis. Any element with a flex value greater than 0 becomes flexible and flex controls the ratio between the dimensions of different flexible elements. For example, in our case, if <main> had flex: 2 and <footer> had flex: 1, the height of the footer would be twice the height of the content. Same if the values were 4 and 2 instead of 2 and 1, because it’s their relationship that matters.

这个 flex 属性实际上是 flex-growflex-shrinkflex-basis 的简写语法。任何元素只要设置了一个大于 0flex 值,就将获得可伸缩的特性;而 flex 的值就负责控制多个可伸缩元素之间的尺寸分配比例。举例来说,在我们眼前的这个例子中,如果 <main>flex: 2<footer>flex: 1 的话,那么内容区块的高度将是页脚高度的两倍。如果把它们的值从 21 改为 42,结果也是一样的,因为真正起作用的是它们的数值比例

That’s it, no more code required! The perfect sticky footer (same visual result as in Figure XX.XX), with only four simple lines of code. Isn’t Flexbox beautiful?

这样就可以了!我们只需要四行简单的代码,就完美实现了紧贴底部的页脚(跟 图 7.25 中的效果一致)。Flexbox 是不是相当霸气?

{试一试} play.csssecrets.io/sticky-footer


{致敬}

Hat tip to Philip Walton for coming up with this technique.

Philip Walton 脱帽致敬,感谢他提出 这个技巧


Related specs

相关规范

@xiaobaicaistring
Copy link

CSS 2.1下有什么好得解决方案么

@cssmagic
Copy link
Owner Author

cssmagic commented Nov 4, 2015

@xiaobaicaistring
可以试试文章开头引用的那些链接。

@cssmagic
Copy link
Owner Author

微博网友 @残阳映枫红 反馈:

“a footer with any block-level styling, such as a background or shadow”

我理解的是,原作者想要强调的是页脚具有块级样式,至于块级样式是什么无所谓,所以就举例背景和阴影好了。因为重点是块级样式造成这种视觉上的不紧贴视口,假如没有背景或者阴影而是边框,一样会出现这种情况。因此您翻译的“有一个页脚,它的整个块级范围都填满了一层背景或阴影”可能理解下来是说这个页脚必须得有个背景或者阴影,而且还得铺满,与原意有所出入。我简单译为“一个块级样式的页脚,比如设置了背景或阴影”。

@joseph19921003
Copy link

好期待这本书!

Specifically, the issue appears on pages whose content is shorter than the viewport height minus the footer height.
具体来说,当页面内容的长度小于视口高度减去页面高度时,这个问题就会出现。

这里是笔误吗?视口高度减去页脚高度

@cssmagic
Copy link
Owner Author

@joseph19921003
谢谢反馈,已修正!

@cssmagic cssmagic mentioned this issue Dec 21, 2015
@macsen110
Copy link

vh, vw在移动短的兼容性好么

@cssmagic
Copy link
Owner Author

@macsen110
http://caniuse.com/#search=vh
需要 Android 4.4+。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants