問題拋出
當頁面上滑時,頂部當導航需要進行吸頂處理。滑動過程使用scroll事件監聽,當滾動的高度到底一定時,便設置樣式 absolute:fixed;top:0px;
以吸頂處理。
以上處理在安卓中時完全沒有問題的,但是在iOS中缺會出現嚴重的樣式問題:頁面滾動過程中並不吸頂。
解決思路
針對iOS做兼容處理。iOS支持一個特殊的定位方式:absolute:sticky;//粘性定位。
sticky 定位
sticky 英文字面意思是粘,粘貼,所以可以把它稱之爲粘性定位。
position: sticky; 基於用戶的滾動位置來定位。
粘性定位的元素是依賴於用戶的滾動,在 position:relative 與 position:fixed 定位之間切換。
它的行爲就像 position:relative; 而當頁面滾動超出目標區域時,它的表現就像 position:fixed;,它會固定在目標位置。
元素定位表現爲在跨越特定閾值前爲相對定位,之後爲固定定位。
這個特定閾值指的是 top, right, bottom 或 left 之一,換言之,指定 top, right, bottom 或 left 四個閾值其中之一,纔可使粘性定位生效。否則其行爲與相對定位相同。
div.sticky {
position: -webkit-sticky; /* Safari */
position: sticky;
top: 0;
background-color: green;
border: 2px solid #4CAF50;
}
解決辦法
判斷設備類型
const judgeUserAgent = function () {
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
return 'ios'
} else if (/(Android)/i.test(navigator.userAgent)) {
return 'android'
} else {
return 'pc'
}
}
安卓的吸頂處理
positionSticky (el, top = 0, zIndex = 10) {
const ot = el.offsetTop - top
const fn = () => {
const st = document.body.scrollTop || document.documentElement.scrollTop
const fixStyle = `position: fixed;top: ${top}px;left: 0px;z-index: ${zIndex};`
el.setAttribute('style', st > ot ? fixStyle : '')
}
fn()
document.addEventListener('scroll', fn)
}
注意:安卓的fixed定位需要在滾動時沒有問題,但是從relative定位切換到fixed定位後,會脫離文本流,使得切換fixed樣式時頁面會出現閃爍的情況。
解決辦法:在需要的進行fixed定位的dom部分外邊使用一個div將其包裹起來,並賦予相應的高度。
<div style="height:50px;">
<!-->需要進行定位的dom部分<-->
<div ref="postionDom">
<!-->內容<-->
</div>
</div>
// vue中mounted鉤子
mounted () {
const el = this.$refs.postionDom
this.positionSticky(el, 0, 10)
}
iOS的吸頂處理
positionSticky (el, top = 0, zIndex = 10) {
if (window.screen.height === 812 && window.screen.width === 375) { // 在iphonex中打開
top += 20
}
const fixStyle = `position: -webkit-sticky;position: sticky;top: ${top}px;left: 0px;z-index: ${zIndex};`
el.setAttribute('style', fixStyle)
return
}
<!-->需要進行定位的dom部分<-->
<div ref="postionDom">
<!-->內容<-->
</div>
// vue中mounted鉤子
mounted () {
const el = this.$refs.postionDom
this.positionSticky(el, 0, 10)
}
代碼整合
注意:兼容處理依然需要將不同設備類型的代碼整合起來,這樣易於調用與維護。
最終代碼
const judgeUserAgent = function () {
if (/(iPhone|iPad|iPod|iOS)/i.test(navigator.userAgent)) {
return 'ios'
} else if (/(Android)/i.test(navigator.userAgent)) {
return 'android'
} else {
return 'pc'
}
}
const positionSticky = function(el, top = 0, zIndex = 10) {
const userAgent = judgeUserAgent()
if (userAgent === 'ios') {
// 雖然iOS中不需要在dom外邊包裹一層,但是爲了兼容android。
// 注意,sticky定位元素不能在其外層的dom元素使用div進行包裹起來,否則sticky會失效。定位到parentNode剛好也避過這個坑
el = el.parentNode
if (window.screen.height === 812 && window.screen.width === 375) { // 在iphonex中打開
top += 20
}
const fixStyle = `position: -webkit-sticky;position: sticky;top: ${top}px;left: 0px;z-index: ${zIndex};`
el.setAttribute('style', fixStyle)
return
}
const ot = el.offsetTop - top
const fn = () => {
const st = document.body.scrollTop || document.documentElement.scrollTop
const fixStyle = `position: fixed;top: ${top}px;left: 0px;z-index: ${zIndex};`
el.setAttribute('style', st > ot ? fixStyle : '')
}
fn()
document.addEventListener('scroll', fn)
}
<div style="height:50px;">
<!-->需要進行定位的dom部分<-->
<div ref="postionDom">
<!-->內容<-->
</div>
</div>
// vue中mounted鉤子
mounted () {
const el = this.$refs.postionDom
this.positionSticky(el, 0, 10)
}