【前端詞典】滾動穿透問題的解決方案

小生方勤發表於2019-01-06

背景

產品有三寶,彈窗,浮層加引導;
設計有三寶,透明,陰影加圓角;
運營有三寶,簡訊,推送加紅包;
程式設計師有一寶,這個做不了。

隨著移動端市場的份額越大,需求就越多樣化。我們今天討論的是移動端的滾動穿透問題。上面這段調侃的話可以看出需求中彈窗浮層還是挺常見的,那這個和滾動穿透有什麼聯絡呢?

我先解釋下什麼是滾動穿透

頁面滑出了一個彈窗,我們用手指觸控螢幕滑動時,會發現彈窗下面的內容還是在滾動。這個現象就是滾動穿透

接下就說下我對滾動穿透問題解決方案探索的過程,希望對大家有點啟發。

需求

需求: 希望在點選圖片的時候,從下方彈一個全屏的彈框來描述這張圖片的詳情。

方案

接到這個需求覺得沒有難度,很快就提測了,然後就開始逛逛掘金。可剛看大佬們的文章看的開心的時候,測試就在微信我。心想來 bug 了?

這是一張聊天截圖
突然意識到寫彈窗的時候忘記處理滾動穿透的問題了。記得第一次遇到這個問題的時候也是找了很久的資料。

方案一:

找到的第一個方法就是當彈窗觸發的時候,給 overflow: scroll: 的元素加上一個 class (一般都是 body 元素)。退出的時候去掉這個 class。下面為了方便,會直接用 body 元素來代指彈窗下方的元素。

// css 部分
modal_open {
    position: fixed;
    height: 100%;
}

// js 部分
document.body.classList.add('modal_open');
document.body.classList.remove('modal_open');
複製程式碼

上面的這個方法可以解決滾動穿透問題,卻也會帶來新的問題。
即:

body 的滾動位置會丟失,也就是bodyscrollTop 屬性值會變為 0。

這個新問題比起滾動穿透本身來說更加麻煩,所以這個方案是要進行優化的。

方案二:

既然新增 modal_open 這個 class 會使 body 的滾動位置會丟失,那麼我們為什麼不在滾動位置丟失之前先儲存下來,等到退出彈窗的前在將這個儲存下來的滾動位置在設定回去。然後就朝著這個方向開始 coding 。

// css 部分
.modal_open {
  position: fixed;
  height: 100%;
}

// js 部分
/**
 * ModalHelper helpers resolve the modal scrolling issue on mobile devices
 * https://github.com/twbs/bootstrap/issues/15852
 */
var ModalHelper = (function(bodyClass) {
    var scrollTop;
    return {
        afterOpen: function() {
            scrollTop = document.scrollingElement.scrollTop  ||
                        document.documentElement.scrollTop || 
                        document.body.scrollTop;
            document.body.classList.add(bodyClass);
            document.body.style.top = -scrollTop + 'px';
        },
        beforeClose: function() {
            document.body.classList.remove(bodyClass);
            document.scrollingElement.scrollTop = document.documentElement.scrollTop = document.body.scrollTop = scrollTop;
        }
    };
})('modal_open');

// method
modalSwitch: function(){
    let self = this;
    if( self.switchFlag === 'close' ){
        ModalHelper.afterOpen();
        self.switchFlag = 'open';
    }else{
        ModalHelper.beforeClose();
        self.switchFlag = 'close';
    }
}
複製程式碼

方案二可以達到以下效果:

  1. 彈窗滾動的時候,下方的 body 是固定的無法滾動;
  2. body 的滾動位置不會丟失;
  3. body 有 scroll 事件;

方案二可以適應絕大多數的彈窗需求,提測後測試方也沒有在提其他問題,這個問題算是完美的解決了。不過我在這個過程有一個疑問:

IOS 自有的橡皮筋效果會導致頁面會出現短暫卡頓現象,暫時沒有找到原因,請教各位。

其他方案:

使用 preventDefault 阻止瀏覽器預設事件:

var modal = document.getElementById('modalBox');
modal.addEventListener('touchmove', function(e) {
    e.preventDefault();
}, false);
複製程式碼

這個方案只適用於這個彈窗本身的高度小於螢幕的高度,即不可滾動的時候。touchmovetouchstart 更加合適。因為 touchstart 會連點選事件都阻止。

使用外掛:

對於外掛我的態度是,除非是自己實現起來太複雜,否則還是自己花點時間去實現。原因有二:

  1. 使用外掛就意味著需要引入的檔案至少多了一個。
  2. 外掛過多,擔心日後專案升級維護成本加大。

以上。

參考

  1. developer.mozilla.org/en-US/docs/…
  2. uedsky.com/2016-06/mob…

前端詞典系列

《前端詞典》這個系列會持續更新,每一期我都會講一個出現頻率較高的知識點。希望大家在閱讀的過程當中可以斧正文中出現不嚴謹或是錯誤的地方,本人將不勝感激;若通過本系列而有所得,本人亦將不勝欣喜。

內容: 前端以及網路相關知識點的介紹並加以實際應用作為輔助。

目的: 這個系列的文章可以對讀者起到一點幫助,解開一些迷惑。

希望各位多指點一二,不吝賜教。

如果你覺得我的文章寫的還不錯,可以關注我的微信公眾號,公眾號裡會提前劇透呦。

【前端詞典】滾動穿透問題的解決方案

下期預告

【前端詞典】繼承 - JavaScript 必懂知識點

傳送門

  1. 【前端詞典】她今天竟然問我什麼是“代理”

相關文章