标签页切换后焦点未落到新面板,需避免v-if/display:none导致dom销毁或隐藏,改用v-show/hidden;聚焦逻辑应置于组件挂载后(vue用onmounted+nexttick,react用uselayouteffect),并确保目标元素存在、可聚焦、防滚动,同步触发且适配aria与移动端safari限制。

标签页切换后焦点没落到新面板?先确认是否用了v-if或display: none
Vue/React 中用 v-if 或 ngIf 切换标签页,组件会销毁重建,autofocus 无效,且首次渲染后 DOM 可能还没 ready。纯 CSS 的 display: none 更麻烦:内部 <input> 切回来时焦点丢失、滚动位置归零,Safari 下 <video></video> 甚至还在缓冲。
- 优先用
v-show(Vue)或hidden属性(原生),保留 DOM 实例 - 若必须用
v-if,聚焦逻辑得放在组件挂载后的生命周期里:Vue 用onMounted+nextTick,React 用useEffect+useLayoutEffect(后者更稳妥) - 别在
setTimeout(() => input.focus(), 100)这种“拍脑袋延时”上赌运气——它在 SSR/hydration 场景下大概率失效
怎么让焦点准确跳到新面板的第一个可操作项?
不能只靠 autofocus,也不能直接 document.querySelector('.tab-pane.active input').focus() —— 面板可能还没渲染完,或者元素压根没 tabindex。
- 目标元素必须可聚焦:
<input>、<button></button>天然支持;如果是<div role="region"> 这类容器,需加 <code>tabindex="-1"才能被.focus()调用成功 - 聚焦前检查存在性:
const el = panel.querySelector('input, button, [tabindex="0"]'); if (el) el.focus(); - 加
{preventScroll: true}防止页面跳动,但 Safari 不支持该选项,要先检测:if ('preventScroll' in FocusOptions.prototype) - 若面板含多个输入框,聚焦第一个语义最重的(如搜索框、主表单字段),别硬写
querySelector('input')拿到隐藏字段 - 切换后不仅要设
panel.setAttribute('aria-hidden', 'false'),还得给对应 tab 按钮设setAttribute('aria-selected', 'true') - 新面板需有
tabindex="0"(仅当它是非原生可聚焦容器时),否则 Tab 键从上一个 tab 按钮跳出来,会直接越过整个面板区域 - 如果面板里第一个可聚焦项是
<button></button>,它不需要额外tabindex;但若你用<div role="button"> 模拟按钮,就必须加 <code>tabindex="0"+keydown监听 - 动态插入的 tab(比如 fetch 后渲染)会导致初始
querySelectorAll('[data-tab]')漏节点,事件得用委托或等数据就绪再绑定 - 确保聚焦代码在用户事件处理函数内**同步执行**,不要跨微任务(
Promise.then)、宏任务(setTimeout) - 实在绕不开异步(如等待 API 返回后再显示带输入框的面板),可临时加
readonly再聚焦:input.setAttribute('readonly', ''); input.focus(); input.removeAttribute('readonly');(iOS 15+ 有效) - 别在
scroll、resize或轮播自动切换回调里调用focus(),浏览器会静默拦截 - 测试时别只点鼠标——真机上用手指 tap,观察是否触发软键盘;模拟器有时行为失真
用 data-tab 关联标签和面板时,焦点接管容易漏掉什么?
用 data-tab="projects" 和 data-tab-panel="projects" 匹配比靠 id 更安全,但 JS 找到面板后,常忽略 ARIA 状态同步和键盘流中断。
移动端 Safari 点击标签后键盘不弹出?这是焦点时机问题
iOS Safari 对非用户手势触发的 focus() 极其严格——哪怕是在 click 回调里异步执行(比如 Promise.then),软键盘也大概率不唤起。
最常被忽略的是:焦点转移不是“让某个元素获得 focus”,而是“让用户知道当前在哪、下一步该操作什么”。所以每次切换,既要确保 JS 调用 .focus() 成功,又要验证屏幕阅读器能否读出新面板标题、Tab 键是否自然落入其中、关闭/返回时能否回退到合理位置——这些都得在真实键盘和 VoiceOver/Speak Screen 下跑一遍。











