Vue3组合式API中如何获取元素offsetTop并实现滚动?
解决Vue3组合式API中动态ref获取元素offsetTop失效的问题
问题核心
你通过v-for给Disclosure组件绑定动态ref时,存入数组的是组件实例,不是DOM元素。直接读取代理后的组件实例的offsetTop自然会失败——这个属性属于DOM元素,而非Vue组件实例。
两种解决方案
方案1:修改Disclosure组件(若有权限)
如果能编辑Disclosure组件的源码,直接在组件内部暴露根DOM元素:
<!-- Disclosure.vue --> <script setup> import { ref, defineExpose } from 'vue' const rootEl = ref(null) // 暴露根元素给父组件 defineExpose({ rootEl }) </script> <template> <div ref="rootEl"> <!-- 给组件根元素绑定ref --> <!-- 组件内部内容 --> </div> </template>
父组件中直接访问暴露的根元素:
<script setup> import { ref, nextTick } from 'vue' import Disclosure from './Disclosure.vue' const accordions = ref([]) const scrollToTarget = async (index) => { await nextTick() // 确保DOM已完成渲染 const targetComp = accordions.value[index] if (!targetComp?.rootEl) return const offsetTop = targetComp.rootEl.offsetTop window.scrollTo({ top: offsetTop, behavior: 'smooth' }) } </script> <template> <Disclosure v-for="(item, idx) in list" :key="idx" :ref="el => el && (accordions.value[idx] = el)" /> <button @click="scrollToTarget(1)">滚动到第二个面板</button> </template>
方案2:直接获取组件的根DOM元素(无需修改组件)
如果无法修改Disclosure组件,Vue组件实例自带的$el属性会指向组件的根DOM元素,直接使用即可:
<script setup> import { ref, nextTick } from 'vue' import Disclosure from './Disclosure.vue' const accordions = ref([]) const scrollToTarget = async (index) => { await nextTick() const targetComp = accordions.value[index] if (!targetComp) return const domEl = targetComp.$el const offsetTop = domEl.offsetTop window.scrollTo({ top: offsetTop, behavior: 'smooth' }) } </script> <template> <Disclosure v-for="(item, idx) in list" :key="idx" :ref="el => el && (accordions.value[idx] = el)" /> <button @click="scrollToTarget(0)">滚动到第一个面板</button> </template>
关键注意事项
- 动态ref的回调要判断
el是否存在:组件卸载时el会变为null,避免把null存入数组导致后续报错。 - 必须用
nextTick:Vue的DOM更新是异步的,尤其是初始化或数据更新后立即调用滚动时,要等DOM渲染完成才能正确获取offsetTop。 - 第三方组件特殊情况:如果Disclosure是第三方UI组件,部分组件可能会把根元素包裹在多个容器里,此时可以通过
$el.querySelector定位到目标元素,或者查看组件文档是否有官方提供的ref获取方式。
内容的提问来源于stack exchange,提问作者Sascha
相关产品推荐
相关产品推荐

