Vue3组合式API中如何在v-for循环里用模板引用移动焦点?
问题
使用按钮显示/隐藏列表项,希望点击按钮时通过ref聚焦到第一个“额外显示”的项(第6个,对应索引5)。在Vue选项式API中可以正常实现,但组合式API里报错:Uncaught TypeError: Cannot read properties of undefined (reading 'value')。Vue开发者工具中可见listItem是包含12个元素的Ref数组,但点击按钮时就是无法正确引用目标元素。
已尝试操作:
- 在
showFullList中尝试listItem[5].value.focus()和listItem.value[5].focus()两种写法 - 在
onMounted()中打印listItem和listItem.value[5]到控制台
原代码:
<script setup> import { ref } from 'vue' const listItem = ref([]) const showAllCounties = ref(false) const showhideItems = (index) => { if (!showAllCounties.value) { return index < 5 } else { return true } } const showFullList = () => { document.activeElement.blur() showAllCounties.value = !showAllCounties.value if (listItem) { console.log('yes') // returns 'yes' console.dir(listItem) // shows proxy array with all 12 items } const target = listItem[5] if (target) { target.value.focus() console.log('yes, ' +target) } else { console.log('no') } listItem[5].value.focus() //also tried listItem.value[5].focus() } </script> <template> <ul> <li v-for="(county, index) in project.data.counties" v-show="showhideItems(index)" class="project-counties-list" ref="listItem" :key="county.id" > {{ county.county_name }}, {{ index }} </li> </ul> <button type="button" @click="showFullList">{{ ButtonText }}</button> </template>
解决方案
核心问题
- DOM更新滞后:修改
showAllCounties.value后,Vue不会立刻更新DOM,此时第6个<li>还处于隐藏状态,DOM元素未完成渲染/显示,直接调用focus()会找不到有效元素。 - Ref数组访问错误:组合式API中,v-for绑定的ref数组,正确访问路径是
listItem.value[index]——listItem是Ref对象,.value才是存储DOM元素的数组,数组内每一项直接是DOM元素,不需要再取.value。
修复步骤
- 用
nextTick()等待DOM更新完成后再执行聚焦,确保目标<li>已显示。 - 修正Ref数组访问方式,直接通过
listItem.value[5]获取DOM元素并调用focus()。 - 增加元素存在性判断,避免报错。
修正后代码
<script setup> import { ref, nextTick } from 'vue' const listItem = ref([]) const showAllCounties = ref(false) const showhideItems = (index) => { return !showAllCounties.value ? index < 5 : true } const showFullList = async () => { document.activeElement.blur() showAllCounties.value = !showAllCounties.value // 等待DOM更新完成 await nextTick() const target = listItem.value[5] if (target) { target.focus() console.log('聚焦成功') } else { console.log('目标元素不存在') } } </script> <template> <ul> <li v-for="(county, index) in project.data.counties" v-show="showhideItems(index)" class="project-counties-list" ref="listItem" :key="county.id" > {{ county.county_name }}, {{ index }} </li> </ul> <button type="button" @click="showFullList">{{ showAllCounties ? '收起列表' : '显示全部' }}</button> </template>
补充说明
nextTick()会在DOM更新周期结束后执行回调,保证操作的是最新DOM状态。- v-for绑定ref时,Vue会自动把所有匹配的DOM元素存入
listItem.value数组,数组顺序和v-for循环顺序一致。 - 如果列表项是异步加载的,需确保数据加载完成后再访问ref数组,可结合
onMounted()或数据加载后的回调处理。
内容的提问来源于stack exchange,提问作者buckthorn
相关产品推荐
相关产品推荐

