一欠雌、背景
最近在做一個(gè)移動(dòng)端的 H5 項(xiàng)目蹄梢,遇到了一個(gè)「有趣」的問(wèn)題。假設(shè)有一頁(yè)面布局如下:
下方 50px 懸浮于底部富俄,采用 fixed 布局禁炒,示例如下:
<div class="container">
<!-- height: 100vh - 50px -->
<div class="page"></div>
<!-- fixed bottom, height: 50px -->
<div class="tabbar">TabBar</div>
</div>
<script>
window.onload = function () {
const arr = new Array(100).fill(0).map((_, index) => index + 1)
const pageEl = document.querySelector('.page')
const listEl = document.createElement('div')
arr.forEach(item => {
const itemElement = document.createElement('div')
itemElement.innerText = item
itemElement.className = 'list'
listEl.appendChild(itemElement)
})
pageEl.appendChild(listEl)
}
</script>
完整示例請(qǐng)看 CodeSandbox齐苛。
測(cè)試下來(lái)看似乎沒(méi)問(wèn)題,可當(dāng)你使用 iPhone 的 Safari 瀏覽器打開此頁(yè)面時(shí)凹蜂,就會(huì)出現(xiàn)如下情況:
截圖中已滑動(dòng)至頁(yè)面最底部,然而 100 被 TabBar 部分擋住了(其他瀏覽器均能正常展示出來(lái))玛痊。
二、原因
我們知道擂煞,vh
、vw
都是 CSS 中的一種相對(duì)長(zhǎng)度單位蝗拿,1vh
表示 viewport 高度的 1%(vm
同理)。簡(jiǎn)單來(lái)講哀托,viewport 基本上是指當(dāng)前文檔的可見部分,因此 100vh
表示可見文檔的最大高度仓手。
可事實(shí)真是如此嗎?
在 Safari 瀏覽器中嗽冒,100vh
如下圖所示(出自):
按上面所說(shuō),100vh
不應(yīng)該是 viewport 可視區(qū)域的全部高度补履,為什么右圖的高度會(huì)超出的呢添坊?
做個(gè)簡(jiǎn)圖區(qū)分一下吧:
所以,這就是為什么在 Safari 會(huì)被擋住一部分的原因干像。
吐槽一下帅腌,Safari 會(huì)是現(xiàn)代化的 IE 瀏覽器?
三麻汰、尋根問(wèn)底
是 bug 還是故意為之速客?
可以詳細(xì)地看下這篇文章:Viewport height is taller than the visible part of the document in some mobile browsers,然后文章作者給 WebKit 提了個(gè) bug五鲫,其中 Apple 工程師 Benjamin Poulain 的回答如下:
This is completely intentional. It took quite a bit of work on our part to achieve this effect. :)
So, it's a feature, not a bug.
然而溺职,并不是只有 Safari 是這樣做的,比如 iOS 端 Chrome 瀏覽器表現(xiàn)與 iOS 端 Safari 一致... (⊙?⊙)
四位喂、解決方法
盡管這并不是大多數(shù)開發(fā)者想要的浪耘,但很無(wú)奈,我們只能想辦法去「修復(fù)」它塑崖,使得我們的網(wǎng)站在各瀏覽器表現(xiàn)一致七冲。
方案一:使用 -webkit-fill-available
簡(jiǎn)單來(lái)說(shuō),-webkit-fill-available 就是自動(dòng)填滿剩余空間(詳見)规婆。這里使用另一個(gè)示例澜躺,來(lái)說(shuō)明 -webkit-fill-available
的一些問(wèn)題蝉稳,如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<style>
* {
padding: 0;
margin: 0;
text-align: center;
}
body {
min-height: 100vh;
display: flex;
flex-direction: column;
}
</style>
<body>
<div style="height: 100px; background: blue"></div>
<div style="flex: 1; background: red"></div>
<div style="height: 100px; background: green"></div>
</body>
</html>
以上示例,分別會(huì)有藍(lán)掘鄙、紅耘戚、綠三部分,按預(yù)期結(jié)果操漠,它們應(yīng)該會(huì)占滿整個(gè)可視區(qū)域(完整示例請(qǐng)看 CodeSandbox)收津,而 Safari 中底部綠色部分會(huì)被擋住。然后我們添加在 body
添加上 -webkit-fill-available
看看:
body {
min-height: 100vh;
min-height: -webkit-fill-available;
display: flex;
flex-direction: column;
}
iOS 上的 Safari 中的表現(xiàn)是正常了浊伙,但是你會(huì)發(fā)現(xiàn) Chrome 下紅色區(qū)域沒(méi)了撞秋,原因是 Chrome 84 起已不再支持 -webkit-fill-available
,所以實(shí)際渲染如下:
那么解決方法就是嚣鄙,針對(duì) Safari 瀏覽器才設(shè)置 -webkit-fill-available
即可部服,這里利用到 @support 和 -webkit-touch-callout,如下:
body {
min-height: 100vh;
display: flex;
flex-direction: column;
}
@supports (-webkit-touch-callout: none) {
body {
min-height: -webkit-fill-available;
}
}
這樣就 OK 了拗慨,Safari 和 Chrome 表現(xiàn)均如預(yù)期一致。這也是 postcss-100vh-fix 插件的解決方案奉芦。
然而赵抢,以上方案并不適用于這些情況烦却,比如:height: 90vh
先巴、height: calc(100vh - 50px)
伸蚯,因此才有了方案二剂邮。
方案二:通過(guò) CSS 變量計(jì)算 1vh 所表示的實(shí)際高度
思路:
設(shè)置一個(gè) CSS 變量(比如
--vh
)挥萌,然后通過(guò) JavaScript 腳本動(dòng)態(tài)設(shè)置--vh
的值引瀑,然后使用時(shí)需兼容處理即可(比如,height: 100vh; height: calc(var(--vh) * 100)
)帜矾。
實(shí)現(xiàn)如下:
<style>
:root {
--vh: 1vh;
}
</style>
<script>
!(function (n, e) {
function setViewHeight() {
var windowVH = e.innerHeight / 100
n.documentElement.style.setProperty('--vh', windowVH + 'px')
}
var i = 'orientationchange' in window ? 'orientationchange' : 'resize'
n.addEventListener('DOMContentLoaded', setViewHeight)
e.addEventListener(i, setViewHeight)
})(document, window)
</script>
使用 vh
時(shí)黍特,需要這樣兼容處理:
.page {
height: calc(100vh - 50px);
height: calc(var(--vh) * 100 - 50px);
}
有一個(gè) react-div-100vh 庫(kù)就是獲取 window.innerHeight
灭衷,然后將其值設(shè)置為容器高度實(shí)現(xiàn)的翔曲,然而這也是僅可處理 100vh
的情況瞳遍。
至于使用哪一種解決方案,視乎實(shí)際情況而定吧由缆!