在现代React应用中,无障碍访问的模态框组件是提升用户体验的重要组成部分。本文将分享一个完整的useAccessibilityModal自定义Hook,它能够帮助开发者轻松实现符合WCAG标准的模态框交互。
核心功能
该Hook主要处理以下无障碍相关逻辑:
- 键盘焦点管理(Tab切换限制)
- ESC键关闭模态框
- 背景滚动锁定
- ARIA属性设置
完整实现代码
import { useState, useEffect, useRef } from 'react';
export const useAccessibilityModal = (isOpen, onClose) => {
const [isMounted, setIsMounted] = useState(false);
const modalRef = useRef(null);
const focusTrapRef = useRef(null);
// 初始化和清理
useEffect(() => {
if (isOpen) {
setIsMounted(true);
document.body.style.overflow = 'hidden';
// 焦点管理
const handleFocusTrap = (e) => {
if (!modalRef.current) return;
const focusableElements = modalRef.current.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
);
if (focusableElements.length === 0) return;
const firstElement = focusableElements[0];
const lastElement = focusableElements[focusableElements.length - 1];
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === firstElement) {
e.preventDefault();
lastElement.focus();
} else if (!e.shiftKey && document.activeElement === lastElement) {
e.preventDefault();
firstElement.focus();
}
}
};
document.addEventListener('keydown', handleFocusTrap);
// 确保模态框获得焦点
setTimeout(() => {
if (modalRef.current) {
modalRef.current.focus();
}
}, 0);
return () => {
document.removeEventListener('keydown', handleFocusTrap);
document.body.style.overflow = 'unset';
};
} else {
setIsMounted(false);
}
}, [isOpen]);
// ESC键关闭
useEffect(() => {
const handleEsc = (e) => {
if (e.key === 'Escape' && isOpen) {
onClose();
}
};
document.addEventListener('keydown', handleEsc);
return () => document.removeEventListener('keydown', handleEsc);
}, [isOpen, onClose]);
// 点击背景关闭
const handleClickOutside = (e) => {
if (modalRef.current && !modalRef.current.contains(e.target)) {
onClose();
}
};
return {
modalRef,
isMounted,
handleClickOutside
};
};
使用示例
function ModalComponent() {
const [isOpen, setIsOpen] = useState(false);
const { modalRef, isMounted, handleClickOutside } = useAccessibilityModal(isOpen, () => setIsOpen(false));
if (!isMounted) return null;
return (
<div
ref={modalRef}
className="modal-overlay"
onClick={handleClickOutside}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
>
<div className="modal-content" onClick={(e) => e.stopPropagation()}>
<h2 id="modal-title">模态框标题</h2>
<p>模态框内容</p>
<button onClick={() => setIsOpen(false)}>关闭</button>
</div>
</div>
);
}
关键优化点
- 使用
useRef避免重复渲染 - 组件卸载时正确清理DOM事件
- 焦点陷阱实现Tab键循环导航
- 保持滚动锁定的正确性
通过这个Hook,开发者可以快速构建符合无障碍标准的模态框组件,提升应用的可访问性和用户体验。

讨论