如何实现仅在有垂直滚动条的页面显示滚动到顶/底按钮
问题原因
你的代码不生效核心有3个问题:
hasVerticalScroll用computed实现,仅在组件初始化时计算一次,后续页面内容动态加载(如接口返回数据渲染、图片加载撑高页面)、窗口尺寸变化时不会重新计算,初始如果没滚动条,后续就算出现滚动条也不会更新状态。- 垂直滚动条判断逻辑不可靠:
document.body.offsetHeight在开启标准DTD、使用Vuetify的v-app布局场景下取值不准,且你把v-scroll指令绑定在靠v-if控制渲染的外层div上,初始无滚动条时div根本不存在,滚动事件永远无法绑定。 - 滚动到底部的逻辑错误:你缓存的
position是点击回顶前的滚动位置,不是页面真实底部位置,会出现点按钮滚不到底的问题。
修复方案
- 把
hasVerticalScroll改成响应式变量,在组件挂载、窗口resize、DOM内容变化、滚动触发时主动重新计算是否存在垂直滚动条 - 直接给window绑定滚动、尺寸变化事件,不要依赖自定义指令绑在局部元素上,组件卸载时记得解绑事件避免内存泄漏
- 修正滚动条判断、滚动到底部的计算逻辑,兼容标准页面和Vuetify布局场景
- 移除多余的外层包裹div,fixed定位的按钮直接放在模板根节点即可
修复后完整代码
<template> <!-- 固定定位按钮直接渲染,通过v-if控制显示状态 --> <v-btn v-if="hasVerticalScroll && !isVisible" fab fixed bottom right color="primary" @click="toBottom" > <v-icon>mdi-arrow-down-bold-box-outline</v-icon> </v-btn> <v-btn v-if="hasVerticalScroll && isVisible" fab fixed bottom right color="primary" @click="toTop" > <v-icon>mdi-arrow-up-bold-box-outline</v-icon> </v-btn> </template> <script> export default { data() { return { isVisible: false, hasVerticalScroll: false, // 保存DOM监听实例,用于卸载时断开 resizeObserver: null } }, mounted() { // 初始化判断滚动条 this.checkHasVerticalScroll() // 绑定全局事件 window.addEventListener('scroll', this.onScroll) window.addEventListener('resize', this.checkHasVerticalScroll) // 监听DOM内容变化,处理动态加载内容撑高页面的场景 this.resizeObserver = new MutationObserver(() => { this.checkHasVerticalScroll() }) this.resizeObserver.observe(document.body, { childList: true, subtree: true, attributes: true }) }, beforeUnmount() { // 组件卸载时解绑事件、断开监听,避免内存泄漏 window.removeEventListener('scroll', this.onScroll) window.removeEventListener('resize', this.checkHasVerticalScroll) if (this.resizeObserver) { this.resizeObserver.disconnect() } }, methods: { // 检查是否存在垂直滚动条 checkHasVerticalScroll() { // 优先取html元素的滚动高度,兼容标准渲染模式 const totalHeight = document.documentElement.scrollHeight const viewHeight = window.innerHeight this.hasVerticalScroll = totalHeight > viewHeight // 如果当前没有滚动条,重置按钮状态 if (!this.hasVerticalScroll) { this.isVisible = false } }, onScroll() { // 滚动时同步更新按钮状态,同时重新校验滚动条(应对滚动加载内容的场景) this.checkHasVerticalScroll() this.isVisible = window.scrollY > 500 }, toTop() { window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) }, toBottom() { // 真实底部位置 = 总滚动高度 - 视口高度 const bottomPos = document.documentElement.scrollHeight - window.innerHeight window.scrollTo({ top: bottomPos, behavior: 'smooth' }) } } } </script>
额外说明
- 如果你使用的Vuetify版本开启了自定义滚动容器(比如给
v-main设置了scrollable属性),需要把代码里的window换成对应滚动容器元素,scrollHeight、滚动位置取值、事件绑定目标对应调整即可 - 代码中500px的滚动触发阈值可以根据你的业务需求自行修改
内容的提问来源于stack exchange,提问作者Gavin Teo Juen
相关产品推荐
相关产品推荐

