原文链接:Some Things You Oughta Know When Working with Viewport Units,by Chris Coyier
David Chanin 写了一篇 简短的文章 总结了在手机端为元素设置 height: 100vh 带来的一个问题。
可以总结为下图:
我们将 一个元素定位在了
_100vh_ 高的元素的底部。带来的问题是,Chrome 浏览器没有把地址栏考虑在内,导致地址栏显示时,迫使底部元素超出视口之外了。
<div class="full-page-element">
<button>Button</button>
</div>
.full-page-element {
height: 100vh;
position: relative;
}
.full-page-element button {
position: absolute;
bottom: 10px;
left: 10px;
}
假设 .full-page-element 是页面里的第一个元素,页面也没发生滚动。我们期望的是看见这个按钮是在可视区域的底部、沿着 100vh 元素的底部边缘显示的。但是由于地址栏的显示,导致这个元素超出视口之外看不见了,在 iOS Safair 和 Android Chrome 中都能看到这个效果。
我经常会使用下段代码:
body {
height: 100vh; /* 不用考虑父元素高度,直接设置就能生效 */
margin: 0;
}
这是一个快速的、在不涉及任何其他元素的情况下,设置 body 为全高(full height)的方式。我通常会在低风险的演示 demo 上这么做,但是这还是有问题的,因为随着浏览器地址栏的出现和消失,页面可能会发生跳动,或者说页面可能不会像我想象中的那样展示。
你可能想到使用 body { height: 100% } 来解决问题,但这也有一个 问题。body 是 <html> 的子元素,而 <html> 的高度默认是由内容撑开的。
如果你需要设置 body 为全高,也需要同时设置 <html> 才行:
html, body {
height: 100%;
}
……这并不是什么大不了的事情,它具有可靠的跨浏览器一致性。
💡 译注:
这是作者要讲的 第一个问题——
100vh没有将手机端的地址栏计算在内,导致地址栏出现时,相对于 100vh 元素定位在底部的元素会因为地址栏的出现,被推出到视口之外。这个问题的解决办法是改用
html, body { height: 100%; }声明解决。
能看出来,底部边缘的定位是很复杂的。再来看看上面 button 元素的定位代码:
.full-page-element button {
position: absolute;
bottom: 10px;
left: 10px;
}
button 元素是 position: absolute 定位的,当因为父元素 .full-page-element 100vh 的设置出现问题时,那么相对于它进行底部定位的按钮自然也会有问题。
如果我们有在屏幕底部定位元素的需求(比如说一个固定导航),大家可能会使用 position: fixed; bottom: 0; 来解决,这看起来 没什么问题,浏览器也会按照我们预期的工作。
💡 译注:
这是作者要讲的 第二个问题—— 屏幕底部的固定定位,应该使用
position: fixed来实现,而不是position: absolute定位。
水平视口单元同样是怪异、有问题的,Windows 系统里的页面 滚动条 通常会占用可视空间,而 100vw 的计算是没有把滚动条计算在内的。换句话说,100vw 会以一种你意想不到的方式导致水平滚动。
也就是说,视口单位(Viewport Units)并不能反映是的视口尺寸(viewport's size)。
如果页面存在水平滚动条,那么下面的代码就会导致水平滚动:
.wrapper {
width: 100vw; /* 这样会有问题 */
}
为了排除滚动条带来的影响,减去它的宽度就可以了。
.wrapper {
width: calc(100vw - var(--scrollbar-width)); /* 这样就没有问题了 */
}
💡 译注:
这是作者要讲的 第三个问题—— Windows 系统中的滚动条是占据空间的,这个会导致 width: 100vw 的设置带来问题,如果还坚持使用此方式的话,不要忘记减去滚动条的宽度。
这个方式的具体讲解,请参考《100vw 下滚动条引发的问题》这篇文章。
(完)