移動端滾動穿透解決方案

jiangxueyang發表於2018-03-31

移動端開發時經常會碰到有全屏遮罩層的彈窗滾動穿透問題,網上大部分的解決方案是彈窗出現時給body絕對定位或者高度設定為100%,並隱藏超出部分。

但是這些解決方法,都存在這樣的問題:當頁面有滾動時,彈出彈窗,頁面會滾動至頂部。特別是當頁面有其他根據document文件滾動距離而絕對定位元素時,會導致頁面元素混亂。

我經過查詢相關文件和實踐,發現可通過監聽touchmove和touchstart事件,來達到背景不可滾動而彈出窗可滾動的目的。可看此demo,建議在移動裝置上檢視。

下面詳細講解一下實現方法:

阻止彈窗的touchmove預設事件

當阻止了整個彈窗的touchmove預設事件後,背景將再也不會滾動。

var node = document.querySelector('.forbid-scroll-wrap');
node.addEventListener('touchmove', function (e) {
    e.preventDefault();
}, false);
複製程式碼

但是這樣會導致彈窗內部也無法滾動。所以需要判斷觸發touchmove事件的元素是否為可滾動區域,程式碼優化如下:

var node1 = document.querySelector('.can-scroll-wrap'),
node2 = document.querySelector('.forbid-scroll-wrap');
node2.addEventListener('touchmove', function (e) {
    var target = e.target;
    if ($(target).parents(canScrollWrap).length === 0 && $(target) != node1) {
        e.preventDefault();
    }
}, false);
複製程式碼

允許彈窗內部滾動

以上只是解決了一個問題:滑動彈窗其他地方背景頁面確實未跟隨滾動,但是沒去彈窗可滾動區域到底部或頂部後,再滑動,背景頁面仍可跟隨滾動。

所以仍需要對可滾動區域的滑動事件做監聽:若向上滑動時,已到底部,或向下滑動時已到頂部,則阻止滑動事件。程式碼如下:

node1.scrollTop = 0;
var offsetHeight = node1.offsetHeight,
    scrollHeight = node1.scrollHeight;

node2.addEventListener('touchmove', function (e) {
    var target = e.target;
    if ($(target).parents(canScrollWrap).length === 0 && $(target) != node1) {
        e.preventDefault();
    }
}, false);

node1.addEventListener('touchmove', function (e) {
    var changedTouches = e.changedTouches, canMove = false;
    var scrollTop = this.scrollTop;
    if (changedTouches.length > 0) {
        var touch = changedTouches[0] || {};
        var moveY = touch.clientY;
        if (moveY > startY && scrollTop <= 0) {
            canMove = false;
        } else if (moveY < startY && scrollTop + offsetHeight >= scrollHeight) {
            canMove = false;
        }else{
            canMove = true;
        }
        if (!canMove) {
            e.preventDefault();
        }
    }

}, false);

node1.addEventListener('touchstart', function (e) {
    var targetTouches = e.targetTouches || [];
    if (targetTouches.length > 0) {
        var touch = targetTouches[0] || {};
        startY = touch.clientY;
    }
}, false)

複製程式碼

解除禁止

彈窗關閉後,可解除所有禁止

node1.addEventListener('touchstart',null,false);
node1.addEventListener('touchmove',null,false);
node2.addEventListener('touchmove',null,false);
複製程式碼

點此檢視原始碼

相關文章