iter(g102): 焦点陷阱下沉为共享助手
This commit is contained in:
@@ -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 到末位回绕到开头
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user