11月24, 2016

弹窗之滚动条处理

弹窗的定位一般有两种:

  • 绝对定位(absolute),用于内容超过一屏的弹窗,top可以由使用者自己定义
  • 固定定位(fixed),用于普通的弹窗,内容小于一屏的。

针对固定定位的弹窗,通常在显示时,不希望页面还能滚动,所以我们要封装一个js,让页面disable scroll和enable scroll。

因此就有了下面的代码:

const keys = { 37: 1, 38: 1, 39: 1, 40: 1 };

function preventDefault(e) {
    e.preventDefault();
}

function preventDefaultForScrollKeys(e) {
    if (keys[e.keyCode]) {
        preventDefault(e);
        return false;
    }
}

let oldonwheel, oldontouchmove, oldonkeydown, isDisabled;

function disableScroll() {

    oldonwheel = window.onwheel;
    window.onwheel = preventDefault; 

    oldontouchmove = window.ontouchmove;
    window.ontouchmove = preventDefault; 

    oldonkeydown = document.onkeydown;
    document.onkeydown = preventDefaultForScrollKeys;
    isDisabled = true;

}

function enableScroll() {
    if (!isDisabled) return;

    window.onwheel = oldonwheel; // modern standard

    window.ontouchmove = oldontouchmove; // mobile

    document.onkeydown = oldonkeydown;
    isDisabled = false;

}

export default {
    disableScroll,
    enableScroll
}

粗粗一看,好像可行。

但是上面的代码有一个问题是,当弹窗的内容有滚动条时,就无法滚动了。

我记得之前的同事有碰到过类似的问题,所以就问了一下,他给了我一段代码:

function _test_wheel(e) {
   var event=e || window.event,
            $target = $(event.target || event.srcElement);
    if ($target.hasClass("mask") || $target.parents(".mask").length) {
        return;
    }
    scroll_act(event.originalEvent.wheelDelta ? event.originalEvent.wheelDelta < 0 : event.originalEvent.detail>0);
}

就是通过判断event.target是不是弹窗的内容区域,如果是,则return,不做preventDefault处理。

于是乎,我也这样实现了一下。

但是又发现一个bug,就是当弹窗内容滚动到底后,再继续往下滚,会触发document的滚动条滚动。当滚动到内容顶点,继续往上滚动,也会触发document的滚动条。

想到的方案,就是计算一下,当超过边界时,处理一下。

随后查了一下,发现有一篇好文:由弹出层引发对滚动原理的讨论

里面有提及解决的方案,然后发现PC的解决方案比较方便,但移动端似乎就很麻烦。

回过头来看,我去研究了一下之前同事写的代码为什么没有bug,原来是有一句这个代码:

$("body,html").css("overflow-y","hidden");

加上之后,确实是OK了。不过像上面文章提及的,最好加上padding-right,这个值为滚动条的宽度。说到这个值,有一个另类的方法来取:

.dui-dialog-lock-test {
    overflow-y: hidden !important;
}
//注:DOM为我自己封装的一个小类库,里面提供了一些简单的api操作。
function getScrollWidth() {
    let dom = document.documentElement;
    let w1 = dom.clientWidth;
    DOM.addClass(dom, "dui-dialog-lock-test");
    let w2 = dom.clientWidth;
    DOM.removeClass(dom, "dui-dialog-lock-test");
    return w2 - w1;
}

另外,外面不可滚动、弹窗内部可以滚,可以通过以下的方式来实现:

function stopPropagation(ev) {
    ev.stopPropagation();
}

$("#popupLayer").on("mousewheel", stopPropagation);
$("#popupLayer").on("touchmove", stopPropagation);

它的实现原理是阻止事件冒泡到document上,这样就不会调用到e.preventDefault()就不会阻止浏览器默认的滚动行为了。

这样书写,也不会侵入之前的代码,较完美。

本文链接:www.my-fe.pub/post/popup-scroll-control.html

-- EOF --

Comments

评论加载中...

注:如果长时间无法加载,请针对 disq.us | disquscdn.com | disqus.com 启用代理。