前言
现在有如下的 HTML 结构:
<body>
<div class="header">Header</div>
<div class="content">Content</div>
<div class="footer">Footer</div>
</body>
如果我们想给页面一个最小高度,保证 Header 和 Footer 分别位于页面的顶部和底部,我们通常会这样写样式:
* {
padding: 0;
margin: 0;
}
body {
min-height: 100vh;
/* flex 布局 */
display: flex;
flex-direction: column;
}
.content {
background-color: skyblue;
flex: 1;
}
在浏览器中我们预览的效果如下:
但是如果在移动端使用某些浏览器,比如 iOS 的 Safari,就会出现纵向滚动条:
这是因为某些移动端浏览器在计算 vh 时,会将工具栏高度也计算进去,因此会出现滚动条,我们可以通过如下三种方案去尝试修复这个行为。
方案一:使用 -webkit-fill-available
-webkit-fill-available
是 webkit 浏览器独有的一个属性值,表示填充剩余可用空间,因此我们可以将 body
的 min-height
设置为该值,就可让 body 填充整个视口了:
body {
/* 不支持 -webkit-fill-available 的回落到 100vh */
min-height: 100vh;
min-height: -webkit-fill-available;
/* flex 布局 */
display: flex;
flex-direction: column;
}
.content {
background-color: skyblue;
flex: 1;
}
设置后我们发现 safari 的高度正常了,但是 PC 和 Android 的 Chrome 浏览器高度却不对了:
这是由于 -webkit-fill-available
两者的表现形式不统一造成的:
- 在 Safari 中:如果设定了
height: -webkit-fill-available
元素父级元素设定了绝对的宽高,那么其元素高度就是父级元素的高度。如果父级元素没有宽高,那其高度就是视口的宽度,可用于替代100vh
让移动端不出现滚动条; - 在 Chrome 中:只有 html 元素设置了
height: -webkit-fill-available
才会填充整个视口宽度,如果想让子元素的高度也为视口高,那就需要层层设置height: -webkit-fill-available
。
因此如果想要兼容 Chrome 的表现,就需要将 CSS 修改为:
html {
height: -webkit-fill-available;
}
body {
/* 不支持 -webkit-fill-available 的回落到 100vh */
min-height: 100vh;
min-height: -webkit-fill-available;
/* flex 布局 */
display: flex;
flex-direction: column;
}
.content {
background-color: skyblue;
flex: 1;
}
注意:html 必须设置 height 而不能设置 min-height,这是因为 body 要继承 html 的高度,如果不指明,子元素的
min-height: -webkit-fill-available
就相当于min-height: auto
。
该方法较为简单,CSS 兼容性也尚可,但是如果元素嵌套过深,比如:
<body>
<div class="container">
<div class="header">Header</div>
<div class="content">Content</div>
<div class="footer">Footer</div>
</div>
</body>
我们想为 .container
设置 min-height
为 -webkit-fill-available
,那就必须将 body
、html
的 height
设置为 -webkit-fill-available
,这样 .cotainer
的最小高度才能在 Chrome 中生效。
参考:《CSS fix for 100vh in mobile WebKit》
方案二:使用 window.innerHeight
window.innerHeight
可以用于获取视口高度,在移动端浏览器中,该值不会包含工具栏的高度,因此我们可以通过该值来修正 vh
。
我们可以使用 CSS Var 来创建一个全局变量 --vh
,该变量的值为 window.innerHeight * 0.01
,也就是 1vh
的高度,然后将使用 100vh
的地方替换为 calc(var(--vh, 1vh) * 100)
即可。
代码逻辑如下:
function setVhCssVar() {
const vh = window.innerHeight * 0.01;
// 创建全局变量 --vh
document.documentElement.style.setProperty('--vh', `${vh}px`);
}
setVhCssVar();
window.addEventListener('resize', setVhCssVar);
使用:
body {
min-height: calc(var(--vh, 1vh) * 100);
}
这个方案虽然需要使用 Javascript,但是效果是比较好的,可以完美的替换 100vh
,且不存在浏览器的差异性。
方案三:使用 dvh
dvh
表示动态视口,是一个比较新的 CSS 单位。其可以动态的表示移动端浏览器的视口高度,比如当浏览器存在工具栏、地址栏时,其表示中间的小视口的高度;而当用户向下滑动,或者手动隐藏掉工具栏时,其表示的是隐藏掉栏框后的大视口高度:
因此我们只需要将 vh
替换为 dvh
即可完美解决滚动条问题:
body {
min-height: 100dvh;
}
但是该特性由于较新,考虑兼容性问题的话需要慎重使用: