JavaScript按钮自动聚焦异常求助:点击红按钮无法恢复最后聚焦绿按钮
按钮自动聚焦功能异常修复方案
问题场景
开发复杂应用界面的按钮自动聚焦功能时遇到异常:
- 绿色按钮(
data-focusable="true")为可聚焦按钮,红色按钮(data-focusable="false")不可聚焦 - 需求:点击红色按钮后,1秒后恢复至最后一个被聚焦的绿色按钮
- 当前异常:点击任意按钮后,焦点总是在1秒后回到第一个绿色按钮
- 触发条件:应用内元素失去焦点(非用户鼠标/键盘操作触发)
初始代码
HTML
<button data-focusable="true">Button 1</button> <button data-focusable="true">Button 2</button> <button data-focusable="false">Button 3</button> <button data-focusable="true">Button 4</button> <button data-focusable="false">Button 5</button> <button data-focusable="false">Button 6</button> <button data-focusable="true">Button 7</button>
CSS
button:focus { outline: 2px solid green; } button[data-focusable="true"] { color: green; } button[data-focusable="false"] { color: red; }
JavaScript
var focusOn = document.querySelectorAll('[data-focusable]')[0]; const setFocus = (targetName) => { if (targetName.getAttribute('data-focusable') === 'true') { targetName.focus({ preventScroll: true }); ['focusout', 'blur'].forEach((eventName) => { targetName.addEventListener(eventName, (event) => { focusOn = event.target; if (focusOn.getAttribute('data-focusable') === 'true') { setTimeout(() => { focusOn.focus({ preventScroll: true }); }, 1000); } else { event.target.focus({ preventScroll: true }); } }); }); } } setFocus(focusOn);
尝试的折中方案(未解决)
HTML
<button class="focusable">Button 1</button> <button class="focusable">Button 2</button> <button class="non-focusable">Button 3</button> <button class="focusable">Button 4</button> <button class="non-focusable">Button 5</button> <button class="non-focusable">Button 6</button> <button class="focusable">Button 7</button>
CSS
button:focus { outline: 2px solid green; } button.focusable { color: green; } button.non-focusable { color: red; }
JavaScript
var buttons = document.querySelectorAll('button'); var focusedButton = document.querySelector('button:focus'); buttons.forEach(button => { button.addEventListener('focus', (event) => { if (!event.target.classList.contains('focusable')) { focusedButton.focus(); } else { event.target.focus(); focusedButton = event.target; } }); });
解决思路
1. 初始代码问题分析
- 仅给第一个绿色按钮绑定了
focusout/blur事件,其他绿色按钮无绑定,导致切换到其他绿色按钮后,焦点丢失时仍触发第一个按钮的事件,最终回到第一个按钮 focusout事件存在冒泡特性,易引发多次触发、逻辑混乱的问题
2. 正确实现方案
核心逻辑
- 全局维护变量存储最后一个被聚焦的可聚焦按钮
- 给所有按钮绑定事件,区分处理可聚焦/不可聚焦按钮的焦点变化
- 用
focusin事件跟踪可聚焦按钮的聚焦状态,更新全局记录 - 监听不可聚焦按钮的
focus事件,延迟1秒后恢复到最后一个可聚焦按钮
代码示例
// 初始化最后一个聚焦的可聚焦按钮 let lastFocusedBtn = document.querySelector('[data-focusable="true"]'); // 给所有可聚焦按钮绑定focusin事件,更新最后聚焦记录 document.querySelectorAll('[data-focusable="true"]').forEach(btn => { btn.addEventListener('focusin', () => { lastFocusedBtn = btn; }); }); // 给所有不可聚焦按钮绑定focus事件,延迟恢复焦点 document.querySelectorAll('[data-focusable="false"]').forEach(btn => { btn.addEventListener('focus', () => { setTimeout(() => { lastFocusedBtn?.focus({ preventScroll: true }); }, 1000); }); }); // 处理应用内元素失去焦点的情况(比如焦点移到非按钮元素) document.addEventListener('focusout', (e) => { const nextFocus = e.relatedTarget; // 检查焦点是否移出到非可聚焦按钮的元素 if (!nextFocus || !nextFocus.hasAttribute('data-focusable') || nextFocus.getAttribute('data-focusable') === 'false') { setTimeout(() => { lastFocusedBtn?.focus({ preventScroll: true }); }, 1000); } });
3. 关键注意点
- 使用
focusin而非focus:focusin支持冒泡,可直接给父元素绑定,减少事件绑定数量 - 避免重复绑定事件:确保每个按钮只绑定一次事件
- 判断焦点去向:通过
relatedTarget确认焦点移动目标,避免不必要的焦点恢复 - 容错处理:用可选链
?.防止lastFocusedBtn为空时出现报错
内容的提问来源于stack exchange,提问作者Sparkle
相关产品推荐
相关产品推荐

