@zhencai/vue-focus-scope
v1.0.12
Published
一个基于 Vue 3 的焦点循环管理组件。它通过"哨兵节点"(Sentinel Nodes)技术,在指定的 DOM 作用域内实现键盘 Tab 键的无缝循环导航。
Readme
焦点管理组件
一个基于 Vue 3 的焦点循环管理组件。它通过"哨兵节点"(Sentinel Nodes)技术,在指定的 DOM 作用域内实现键盘 Tab 键的无缝循环导航。
适用于模态框(Modal)、下拉菜单、自定义表单或任何需要限制用户焦点范围的场景,符合 WAI-ARIA 无障碍最佳实践。
功能特性
自动循环导航
- 按
Tab到达最后一个元素时,自动跳回第一个 - 按
Shift + Tab到达第一个元素时,自动跳到最后一个
- 按
智能作用域限制
- 自动过滤不可聚焦元素(
tabindex="-1",disabled) - 自动忽略被
<fieldset disabled>包裹的元素 - 排除内部哨兵节点,防止干扰正常流程
- 自动过滤不可聚焦元素(
双模式支持
loop(默认):强循环模式。焦点被严格限制在组件内部,无法通过 Tab 键移出soft-loop:软循环模式。在首尾之间提供缓冲,允许特定的交互逻辑(防止单元素死循环)
自动聚焦
- 通过
autofocus属性控制组件挂载时是否自动聚焦第一个可聚焦元素 - 支持动态切换,可在组件挂载后通过改变属性值触发聚焦
- 适用于多作用域场景,避免后挂载的组件抢占焦点
- 通过
无障碍友好:原生支持键盘导航,无需额外配置
安装
npm install @zhencai/vue-focus-scope基础用法
<template>
<FocusScope>
<h3>用户登录</h3>
<input type="text" placeholder="用户名" />
<input type="password" placeholder="密码" />
<button @click="submit">登录</button>
<a href="/forgot">忘记密码?</a>
</FocusScope>
</template>
<script setup lang="ts">
import { FocusScope } from "@zhencai/vue-focus-scope";
</script>模式切换
通过 tabMode 属性控制循环行为:
<script setup lang="ts">
import { FocusScope } from "@zhencai/vue-focus-scope";
</script>
<template>
<!-- 强循环:焦点永远无法逃出此区域 -->
<FocusScope tab-mode="loop">
<!-- 内容 -->
</FocusScope>
<!-- 软循环:适合只有一个输入框或需要特殊逃逸逻辑的场景 -->
<FocusScope tab-mode="soft-loop">
<input type="text" placeholder="唯一输入框" />
</FocusScope>
</template>自动聚焦控制
通过 autofocus 属性控制焦点行为:
<script setup lang="ts">
import { FocusScope } from "@zhencai/vue-focus-scope";
import { ref } from "vue";
const showDialog = ref(false);
</script>
<template>
<div>
<!-- 场景1:默认不自动聚焦 -->
<FocusScope :autofocus="false">
<input type="text" placeholder="不会自动聚焦" />
</FocusScope>
<!-- 场景2:挂载时自动聚焦 -->
<FocusScope :autofocus="true">
<input type="text" placeholder="会自动聚焦到这里" />
</FocusScope>
<!-- 场景3:动态控制聚焦(多作用域场景) -->
<button @click="showDialog = true">打开对话框</button>
<FocusScope v-if="showDialog" :autofocus="true">
<input type="text" placeholder="对话框打开时自动聚焦" />
<button @click="showDialog = false">关闭</button>
</FocusScope>
</div>
</template>多作用域场景说明:
- 当页面有多个
FocusScope组件时,为了避免后挂载的组件抢占焦点 - 可以先将所有组件的
autofocus设为false - 在需要聚焦时(如对话框显示、标签页切换),动态将对应组件的
autofocus设为true
API 文档
Props
| 属性名 | 类型 | 默认值 | 说明 |
| :---------- | :---------------------- | :------- | :-------------------------------------------------------------------------------------------------------------------- |
| tabMode | 'loop' \| 'soft-loop' | 'loop' | 循环模式:- loop: 严格限制焦点在插槽内- soft-loop: 允许一定程度的外部交互,主要用于防止单元素死循环 |
| autofocus | boolean | false | 是否自动聚焦第一个可聚焦元素:- true: 组件挂载时或属性变为 true 时自动聚焦- false: 不自动聚焦,需手动控制 |
Slots
| 名称 | 说明 |
| :-------- | :------------------------------------------------------- |
| default | 放置需要被管理焦点的子元素(如 input, button, a 标签等) |
工作原理
该组件利用 Focus Sentinel(焦点哨兵) 技术:
起始哨兵 (sentinel-start)
- 位于插槽内容之前
- 当用户按
Shift + Tab从第一个元素回退时,焦点会落在此处 - 组件拦截该事件,立即将焦点重定向到最后一个可聚焦元素
结束哨兵 (sentinel-end)
- 位于插槽内容之后
- 当用户按
Tab从最后一个元素前进时,焦点会落在此处 - 组件拦截该事件,立即将焦点重定向到第一个可聚焦元素
可聚焦元素检测
- 内部使用
querySelectorAll动态计算当前作用域内的有效焦点元素 - 自动排除
tabindex="-1"、disabled状态以及被禁用的fieldset
注意事项
- 子元素要求:确保插槽内的元素是原生的可聚焦元素(如
<input>,<button>,<a href>)或具有有效的tabindex
许可证
MIT
