如何在Vue.js单文件组件根元素显示(v-show切换后)时设置焦点
实现侧边导航的自动聚焦与失焦隐藏
我来帮你搞定这个需求,核心就是两个关键点:显示时自动聚焦侧边栏根元素,以及通过失焦事件判断是否隐藏侧边栏。下面分步骤给你详细说明:
1. 让侧边栏根div支持聚焦
div元素默认是不能被聚焦的,所以首先要给侧边栏的根div添加tabindex="-1"属性。这个值的作用是:允许通过JavaScript手动聚焦该元素,但不会让它被Tab键遍历到(避免影响页面的无障碍体验)。
同时,我们需要给这个div添加ref,方便后续在脚本中获取DOM元素:
<template> <div v-show="isSideNavVisible" ref="sideNavRoot" tabindex="-1" @blur="handleSideNavBlur" class="side-nav" > <!-- 这里放你的侧边导航内容 --> <h3>导航菜单</h3> <ul> <li>菜单项1</li> <li>菜单项2</li> </ul> </div> </template>
2. 监听Vuex状态变化,显示时自动聚焦
当Vuex中的显示标志变为true时,我们需要在DOM更新完成后(因为v-show是控制元素的display属性,需要等元素真正显示出来),调用focus()方法聚焦根div。
Vue3 版本(Composition API)
<script setup> import { ref, watch, computed, nextTick } from 'vue' import { useStore } from 'vuex' const store = useStore() const sideNavRoot = ref(null) // 从Vuex获取侧边栏显示状态 const isSideNavVisible = computed(() => store.state.isSideNavVisible) // 监听显示状态变化 watch(isSideNavVisible, (newVisibleState) => { if (newVisibleState) { // 用nextTick确保DOM已经更新完成,元素处于显示状态 nextTick(() => { sideNavRoot.value?.focus() }) } }) </script>
Vue2 版本(Options API)
export default { computed: { isSideNavVisible() { return this.$store.state.isSideNavVisible } }, watch: { isSideNavVisible(newVisibleState) { if (newVisibleState) { this.$nextTick(() => { this.$refs.sideNavRoot.focus() }) } } } }
3. 处理失焦事件,判断是否隐藏侧边栏
直接监听根div的blur事件会有一个问题:当你点击侧边栏内部的元素时,焦点会从根div转移到内部元素,这时候也会触发blur事件,但我们不希望此时隐藏侧边栏。
所以我们需要通过事件对象的relatedTarget属性,判断失焦后焦点是否还在侧边栏内部:
- 如果
relatedTarget是侧边栏的子元素,说明是内部切换焦点,不隐藏 - 如果
relatedTarget是外部元素或者null,说明点击了侧边栏外部,触发隐藏
继续完善脚本部分:
Vue3 补充handleSideNavBlur方法
const handleSideNavBlur = (e) => { // 检查失焦后的目标元素是否不在侧边栏内部 if (!sideNavRoot.value?.contains(e.relatedTarget)) { // 提交Vuex mutation修改显示状态为false store.commit('SET_SIDE_NAV_VISIBLE', false) } }
Vue2 补充handleSideNavBlur方法
methods: { handleSideNavBlur(e) { if (!this.$refs.sideNavRoot.contains(e.relatedTarget)) { this.$store.commit('SET_SIDE_NAV_VISIBLE', false) } } }
最后:Vuex的mutation配置
别忘了在你的Vuex store中定义对应的mutation,用来修改显示状态:
// store/index.js export default new Vuex.Store({ state: { isSideNavVisible: false }, mutations: { SET_SIDE_NAV_VISIBLE(state, payload) { state.isSideNavVisible = payload } } })
注意事项
- 如果你的侧边栏内部有需要用户手动聚焦的元素(比如输入框),可以根据需求调整聚焦逻辑,比如优先聚焦内部元素而不是根div
- 可以给根div添加
:focus-visible样式,让用户能看到当前焦点的位置(提升无障碍体验)
内容的提问来源于stack exchange,提问作者Charles Okwuagwu
相关产品推荐
相关产品推荐

