做弹出层组件的时候总是希望背景层停止滚动,但是弹出层需要滚动时还是需要的,介绍两种思路吧,一种是比较常见的 overflow:hidden 方式,另一种是我自己的方法。

隐藏滚动条

既然让背景停止响应滚动,那就隐藏它吧,这就是 overflow 这种方法的思路,所以就如下的代码

<button class="showBtn">显示弹出层</button>
<div class="text">
    <br/>00<br/>01<br/>02<br/>03<br/>04<br/>05<br/>06<br/>07<br/>08<br/>09
    <br/>10<br/>11<br/>12<br/>13<br/>14<br/>15<br/>16<br/>17<br/>18<br/>19
</div>
<div id="back">
    <div class="content">
        <button class="hideBtn">隐藏弹出层</button>
        <div class="text">
            <br/>00<br/>01<br/>02<br/>03<br/>04<br/>05<br/>06<br/>07<br/>08<br/>09
            <br/>10<br/>11<br/>12<br/>13<br/>14<br/>15<br/>16<br/>17<br/>18<br/>19
        </div>
    </div>
</div>
.overflow {
    overflow:hidden;
}
.text {
    font-size:20px;
    line-height:40px;
    width:300px;
    margin:0 auto;
}
#back {
    position:absolute;
    top:0;
    bottom:0;
    z-index:5;
    background:rgba(0,0,0,.4);
    display:none;
    width:100%
}
.content {
    background:#FFF;
    height:400px;
    width:500px;
    margin:30px auto;
    overflow-y:auto;
}
var showLayer = function() {
    $("body").addClass("overflow");
    $("#back").show();
};

var hideLayer = function() {
    $("body").removeClass("overflow");
    $("#back").hide();
};

$(".showBtn").bind( "click", showLayer );
$(".hideBtn").bind( "click", hideLayer );

这种方式有个问题,就是在切换的时候背景层会有一个偏移,感觉震了一下,很不好,原因就是滚动条隐藏了,页面宽度改变,重绘后导致偏移 1/2 滚动条宽度,简单的解决办法就是既然滚动条消失了,那就给那块填充个东西占位吧,所以 CSS 中代码加一条,如下(Windows 中滚动条宽度 17px)

.overflow {
    overflow:hidden;
    padding-right:17px;
}

这种还有一个明显的 Bug,就是 Linux 下,Mac 下的滚动条宽度不定,还有 Opera 还可以隐藏滚动条,Chrome,FireFox 等都能在自定义样式中重新定义滚动条的宽度,所以滚动条的宽度需要用 JavaScript 自动获取,如下

// 检测滚动条宽度
var __scrollBarWidth = null;
function getScrollBarWidth() {
    if (__scrollBarWidth) return __scrollBarWidth;

    var scrollBarHelper = document.createElement("div");
    // if MSIE
    // 如此设置的话,scroll bar的最大宽度不能大于100px(通常不会)。
    scrollBarHelper.style.cssText = "overflow:scroll;width:100px;height:100px;"; 
    // else OTHER Browsers:
    // scrollBarHelper.style.cssText = "overflow:scroll;";
    document.body.appendChild(scrollBarHelper);
    if (scrollBarHelper) {
        __scrollBarWidth = {
            horizontal: scrollBarHelper.offsetHeight - scrollBarHelper.clientHeight,
            vertical: scrollBarHelper.offsetWidth - scrollBarHelper.clientWidth
        };
    }
    document.body.removeChild(scrollBarHelper);
    return __scrollBarWidth;
}
 function test() {
    var scrollBarWidth = getScrollBarWidth();
    alert( "滚动条宽度:横向 :" + scrollBarWidth.horizontal
    + "px,纵向 :" + scrollBarWidth.vertical + "px" );
}

test();

所以最早的版本可以加上这个代码然后做一个比较完美的版本.

PS: 这种检测的现在也发现了一个 Bug 就是在 Opera 下隐藏滚动条设置后这个检测还是能检测出来 17px 的宽度,原因就是 Opera 只是隐藏了最顶层页面的滚动条,而且检测不到,所以现在无解了。

Alpha 自己的方法

我的思路不是隐藏滚动条,是停止触发滚动的操作,所以就是停止鼠标滚轮的操作,这就没有了上面计算滚动条宽度等复杂操作了,看这个 Demo 吧:停止滚动触发

这样的 Bug 也很明显,只能 iframe 框架使用;另一个就是滚动条没隐藏,所以去拖动滚动条页面还是滚动了,但退一步想既然用户在滚轮无效的情况下拖动滚动条也就是有在弹出层显示时滚动背景的这个需求(好怪异的需求),那就不禁止了。

两种思路都有好坏,还是看具体的应用场景了,仅作演示,代码的兼容性没有测试。