前端系列——不同凡響的移動端底部固定欄 fixed、absolute 兼容 iOS 和 Android 方案

相信我,我分享的和你在其餘博客上看到的終極方案是如此的不同凡響!javascript

作過移動端開發的同窗,對底部DOM定位出現的各類奇葩狀況已經深惡痛絕了吧,底部DOM設置不一樣的position,在Android和ios上表現都不同。css

爲了兼容Android和ios,不少人都煞費苦心,也包括我。html

打開你作的H5,尤爲是在微信上打開看看,是否是以爲很噁心,若是自我感受很噁心,那麼請往下看這篇文章,不噁心說明你成功了,能夠走了!java

最終仍是成功解決了,這篇文章記錄一下兼容2種設備的方案。react

第一種狀況

據我所知,網上還找不到一個可以真正解決這個問題的教程,由於大多數人都是隻考慮在body scroll的狀況下,設置底部爲fixed或者absolute,而後設置滾動區域padding-bottom的值,這種作法反正我是沒法接受的,體驗太不爽了,也沒有兼容Android和ios。android

下圖是第一種狀況,滾動區域有表單,底部一個固定欄,當填寫表單的時候,咱們看看ios和Android的表現狀況:ios

一、底部定位爲fixed的狀況下css3

ios:激活輸入框時,底部不會彈出來(合理)。
Android:激活輸入框時,底部會跟着輸入框彈出來(不合理)。git

二、底部定位爲absolute的狀況下github

ios:激活輸入框時,底部不會彈出來(合理)。
Android:激活輸入框時,底部會跟着輸入框彈出來(不合理)。

android後遺症:輸入框失焦的時候,可能致使底部顯示在瀏覽器中間某個位置,回不到原位置。

absolute後遺症:底部按鈕和輸入框區域一塊兒隨着body滾動,再也不置頂獨立。當滾動區域超過一屏幕時,底部輸入框定位出現錯亂。

clipboard.png

傳統解決辦法

一般將底部設置爲fixed,當激活輸入框的時候,將底部定位改成relative,便可兼容ios和Android。

第二種狀況

底部若是是個輸入框的狀況下,咱們確定須要輸入框在激活的時候彈出來,和第一種狀況是相反的。

一、底部定位爲fixed的狀況下

ios:激活輸入框時,底部不會彈出來(不合理)。
Android:激活輸入框時,底部會跟着輸入框彈出來(合理)。

二、底部定位爲absolute的狀況下

ios:當滾動區域超過一屏幕時,底部輸入框定位出現錯亂(不合理)。
Android:當滾動區域超過一屏幕時,底部輸入框定位出現錯亂(不合理)。

clipboard.png

傳統解決辦法:

仍舊是採用fixed定位

ios:在激活輸入框的時候,執行下面代碼

setTimeout(() => document.body.scrollTop = document.body.scrollHeight, 500)

android: 表現正常

傳統解決方案的後遺症

除了抖動問題,還有就是微信端滾動body會顯示微信瀏覽器背景,也就是超出滾動邊界回彈效應,還有一個噁心的問題是當有彈框的時候,彈框和body滾動累加的雙重滾動會有點擊穿透形成的卡頓問題。

由此,若是你還寄但願於body滾動,那麼你的移動端開發體驗真的一塌糊塗。

搭建真正的移動端滾動架構

看到這裏,你能夠暫時把上面的傳統解決方案通通忘記。

下面我將會分享移動端最溫馨的架構方案。

一、你可能聽過Iscroll,這個東西是咱們今天要用到的框架的鼻祖,但咱們不是用它,而是我曾經另一篇文章介紹到的JRoll框架(比IScroll更加輕量和兼容的移動端滾動框架)。

二、使用這款框架對咱們解決底部定位問題還有優化彈框體驗有什麼幫助呢?他能夠完美解決傳統解決方案的後遺症,由於他並非使用body滾動,而是使用css3滾動,採用GPU加速,在ios和Android上測試並不卡頓。若是你想作出像app同樣流程的H5,別再用那噁心的body滾動了。

源碼(複製查看效果,別忘了導入js插件)

下面的源碼你能夠直接複製到一個html文件上測試,代碼中我提供了多種功能的解決方案:

一、採用滾動框架時,什麼時候獲取滾動區域的高度(看源碼)

二、輸入框底部固定時,在該框架中兼容ios和Android的方法(看源碼)

三、採用DocumentFragment動態渲染5000個列表元素,說到這個有點意思,記得騰訊某部門的社招面試題就是考察這個知識點,通常人可能採用的是for循環加innerHTML的方法(看源碼)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=0.5, minimum-scale=0.5, maximum-scale=0.5, user-scalable=no">
    <title>Title</title>
    <style>
        * {
            padding: 0;
            margin: 0;
        }
        body, html {
            font-size: 24px;
            height: 100%;
        }
        ul {
            padding-bottom: 1rem;
        }
        ul li {
            list-style: none;
        }
        .bottom {
            position: fixed;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 4.0833rem;
        }
        .bottom > input {
            width: 100%;
            border: 0;
            outline: 0;
            background: rgb(246, 246, 246);
            color: rgb(255, 255, 255);
            text-align: center;
            line-height: 4.0833rem;
            font-size: 1.25rem;
        }
    </style>
</head>
<body>
<div id="scroll-body">
    <ul></ul>
</div>
<div class="bottom"><input type="text" placeholder="請輸入內容" onfocus="evocation()"></div>
<script src="./js/jroll.js"></script>
<script>
    function getClientHeight() {
//        獲取移動端屏幕高度
        var winHeight
        if (window.innerHeight) {
            winHeight = window.innerHeight;
        } else if ((document.body) && (document.body.clientHeight)) {
            winHeight = document.body.clientHeight;
        } else if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth) {
            winHeight = document.documentElement.clientHeight;
        }
        return winHeight
    }

    var scrollBody = document.querySelector('#scroll-body') //獲取滾動區域的DOM
    var bottom = document.querySelector('.bottom') //獲取底部DOM
    function renderLi() {
        //渲染li列表,採用DocumentFragment方案
        var ul = document.querySelector('ul')
        var dFrag = document.createDocumentFragment()
        var startTime = new Date().getTime()
        for (var i = 0; i < 5000; i++) {
            var li = document.createElement("li")
            li.textContent = i
            dFrag.appendChild(li)
        }
        ul.appendChild(dFrag)
        var endTime = new Date().getTime()
        console.log('渲染耗時:', endTime-startTime, 'ms')
    }
    function evocation() {
        //ios喚出彈框,Android的不須要
        setTimeout(() => document.body.scrollTop = document.body.scrollHeight, 500)
    }
    renderLi()
    document.addEventListener('DOMContentLoaded', function() {
        var height = getClientHeight() - bottom.offsetHeight //獲取滾動區域高度
        scrollBody.style.height = height + 'px' //計算出實際的滾動區域的高度,而後設置
        new JRoll(scrollBody) //實例化JRoll插件
    })
</script>
</body>
</html>

總結

使用上面提供的框架,你在移動端開發中,再也不須要擔憂底部固定的問題,再也不須要擔憂滾動形成的一系列問題,再也不須要擔憂彈框滾動以及點擊彈框形成的穿透問題等。

並且,不知道你發現沒有,底部固定欄你如今能夠嘗試使用fixed、absolute、relative等設置,再也不侷限於只能使用fixed了。感興趣就好好研究一下代碼吧!

可是

在IOS11版本中,我發現了document.body.scrollTop = document.body.scrollHeight無效的bug,目前還沒找到緣由,小於IOS11一切正常。

這裏也封裝了React版本的插件,能夠下載使用:react-roll-container

相關文章
相關標籤/搜索