问题描述

页面添加一个返回顶部的按钮(fixed),并监控窗口的 scroll 滚动事件。当 scrollTop(滚动条距离顶部的距离,及文档滚动距离)在一定范围内(比如视窗高度)隐藏返回顶部按钮;当 scrollTop 超过一定高度,则显示返回顶部按钮。

具体 demo

<div id="back-to-top">
    <img src="./images/back-to-top.png" width="50" height="50" alt="返回顶部">
</div>
<style type="text/css">
    /*返回顶部*/
    #back-to-top{
        position: fixed;
        right:20px;
        bottom:100px;
        z-index: 1;
        display: none;
    }
</style>
<script type="text/javascript">
    //返回顶部
    $(function(){
        $(window).scroll(function(){
            if ($(window).scrollTop() > 300){
                $("#back-to-top").fadeIn(500);
            } else {
                $("#back-to-top").fadeOut(500);
            }
        });
        $("#back-to-top").click(function(){
            $('body,html').animate({
                scrollTop: 0
            }, 800);
        });
    });
</script>

代码添加到现有页面后发现,$(window).scroll() 并未触发。这是 jQuery 的 scroll 绑定事件写法,JavaScript 版本如下:

window.onscroll = function(){

}

使用 Javascript 给 window 添加 onscroll 事件也不成功,说明跟 jQuery 版本无关。
浏览器里 Event Listeners 视窗下,scroll 事件下是存在 Window 这一绑定对象的,说明事件绑定成功。
将 demo 单独页面测试,运行正常,说明 demo 代码本身也没有问题。

解决方法

百度关键词 jquery 绑定 window scroll 事件回调失败,找到一些 scroll 无法触发的解决方案。

其中有一个说因为 html 和 body 宽、高被设置成了 100%,所以无法触发 scroll 事件。demo 测试发现这是不对的。只要文档的高度大于视窗的高度,scroll 事件就可以触发。宽、高与屏幕宽高完全一致的情况下,确实无法触发,但这种情况下也不需要去监控 scroll 事件。demo 测试还发现 $(window).scrollTop() 是随着页面滚动而变化的,$('body').scrollTop() 始终为 0。

window'html''body' 三个对象中,html 绑定 scroll 事件是无法触发的;window 对象没有 s,不加引号;'body' 绑定是需要加引号的。正常情况下,window 的 scrollTop 值随页面滚动变化,body 的 scrollTop 始终为0;当 overflow:auto 时,body 的 scrollTop 值随页面滚动变化,而 window 的 scrollTop 始终为 0。

最终找到了问题的关键点:CSS 的 overflow 与 window.onscroll 发生了冲突

demo 中默认是未设置 overflow 属性的,而目标页面上 overflow:auto。demo 测试重现了这个 bug,说明问题就出在了 overflow 属性上。因为 overflow:auto 时页面是能够正常滚动的,所以并未发觉异常。

测试发现,html 或 body 的 overflow 属性值为 auto/scroll(hidden 都滚不动了,肯定没法触发,不讨论)时,window 的 scoll 滚动事件无法触发。但是,当 overflow 为 auto/scroll 时,将 scroll 绑定在 body 上是可以正常触发的。

综上,解决方法有两个,一个是将 scroll 事件绑定到 body 上,另一个就是将 html,body 上的 overflow:auto/scroll 改成其他的属性值,比如 initialvisibleinherit 等,或者干脆去掉也行。