最近在开发时,遇到一个理论与实践不相符的问题:明明 div 可以滚动,偏偏获取不到它的 scrollTop。
功能:
1.“返回顶部”按钮默认隐藏;
2.当页面往下滚动到 200px 以后时,“返回顶部”按钮出现。
3.当点击“返回顶部”按钮时,页面滚动到顶部。
草图:
思路:
1.一开始让“返回顶部”按钮隐藏,监听 div 滚动事件,到达指定高度后,让按钮出现。
2.“返回顶部”按钮监听点击事件,点击后,回到 div 顶部即可。
本来是 Vue 代码,懒得启动脚手架,直接写原生演示。
页面结构:可以根据 Vue 单页面想象一下,最外层是元素 app,中间的 container 是某路由渲染的模块内容。
<body> <div id="app"> <div class="container"> <h1>app 某模块</h1> <h2>模块总高度:2000px</h2> <p>当前滚动的高度:<span class="current-scrollTop">0</span> px</p> <a class="back-top hide" href="javascript:;">返回顶部</a> </div> </div> </body>
页面样式:
* { margin: 0; padding: 0; box-sizing: border-box; } html, body { height: 100%; overflow: hidden; } #app { height: 100%; border: 5px dashed red; overflow: auto; } #app>.container { height: 2000px; margin: 0 auto; background-color: #abcdef; } #app>.container>h1, #app>.container>h2, #app>.container>p { text-align: center; } #app>.container>p { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); font-weight: 700; } /* 按钮 */ a.back-top { position: fixed; right: 50px; bottom: 50px; display: inline-block; padding: 10px; text-decoration: none; border: 2px solid #000; } /* 控制按钮隐藏 */ a.back-top.hide { display: none; }
页面逻辑:
- 1.开始犯错
const dom = document.getElementsByClassName('container')[0]; dom.addEventListener('scroll', function () { console.log('scrollTop: ', this.scrollTop); }) console.log(dom.scrollTop); // 0
按照上面的写法,始终没有监听到 div 的滚动事件,打印的 scrollTop 也只有一个 0。
- 2.明明页面可以滚动,怎么监听不到事件呢?
- 3.哦!
div.container根本不能滚动,能滚动的是设置了overflow: auto;的 app 元素! - 4.监听 app 元素的滚动事件试一试:
const app = document.getElementById('app'); app.addEventListener('scroll', function () { console.log('scrollTop: ', this.scrollTop); })
成功了,控制台里有数据记录。
- 5.接下来根据设计思路,编写正确的代码就行:
const app = document.getElementById('app'); const dom = document.getElementsByClassName('container')[0]; const btn = document.getElementsByClassName('back-top')[0]; const text = document.getElementsByClassName('current-scrollTop')[0]; app.addEventListener('scroll', function () { text.innerText = this.scrollTop; if (this.scrollTop > 200) { btn.classList.remove('hide'); } else { btn.classList.add('hide'); } }) btn.addEventListener('click', function () { if (app.scrollTop > 200) { app.scrollTo({ top: 0, behavior: 'smooth' }) } })
没错,真相就在这里:由于给 html、body 设置了 overflow: hidden; 属性,视觉上,让我误以为滚动条是 div.container 的,实际上是 #app 的。(当然,Vue 里的代码最好在 unmounted(destroyed) 钩子触发时,将一些事件监听器移除,以免对其他组件造成影响。)

