iter(g102): 焦点陷阱下沉为共享助手

This commit is contained in:
HIsheR
2026-10-26 13:51:40 +08:00
parent f373b8cf31
commit abfbb821b7
5 changed files with 37 additions and 34 deletions
+21
View File
@@ -65,3 +65,24 @@ export function lockScroll(): () => void {
if (locks === 0) document.body.style.overflow = ''; // 全关解锁
};
}
/*** 焦点陷阱:Tab / Shift+Tab 在容器内首尾环形循环,容器内无可用焦点时整体拦截;焦点已在容器外则按方向拉回首尾 ***/
export function trapFocus(box: HTMLElement, isOpen: () => boolean): void {
box.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.key !== 'Tab' || !isOpen()) return; // 卫语句:只处理打开中的 Tab
const els = Array.from(box.querySelectorAll<HTMLElement>('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'))
.filter((el) => !el.hasAttribute('disabled')); // 容器内可聚焦元素(排除禁用)
if (!els.length) { e.preventDefault(); return; } // 卫语句:无可用焦点就整体拦截,防焦点逃逸
const first = els[0];
const last = els[els.length - 1];
const active = document.activeElement;
const outside = !box.contains(active); // 焦点落在容器外:按方向重新拉回,而不是让它继续往外跑
if (e.shiftKey && (active === first || outside)) {
e.preventDefault();
last.focus(); // Shift+Tab 到首位回绕到末尾
} else if (!e.shiftKey && (active === last || outside)) {
e.preventDefault();
first.focus(); // Tab 到末位回绕到开头
}
});
}