You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现仅在有垂直滚动条的页面显示滚动到顶/底按钮

问题原因

你的代码不生效核心有3个问题:

  1. hasVerticalScroll 用computed实现,仅在组件初始化时计算一次,后续页面内容动态加载(如接口返回数据渲染、图片加载撑高页面)、窗口尺寸变化时不会重新计算,初始如果没滚动条,后续就算出现滚动条也不会更新状态。
  2. 垂直滚动条判断逻辑不可靠:document.body.offsetHeight 在开启标准DTD、使用Vuetify的v-app布局场景下取值不准,且你把v-scroll指令绑定在靠v-if控制渲染的外层div上,初始无滚动条时div根本不存在,滚动事件永远无法绑定。
  3. 滚动到底部的逻辑错误:你缓存的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.03 07:57:28