React Material UI Backdrop打开时如何让背景组件可见但不可访问?
React Material UI Backdrop打开时如何让背景组件可见但不可访问?
我之前也碰到过这个问题——Material UI的Backdrop默认只是遮罩层,不会阻止背景元素的键盘或鼠标交互,比如按Tab键还是能切换到后面的按钮、输入框这些元素,确实有点头疼。下面几个方法可以实现让背景元素保持可见,但完全不可访问,不用visibility: hidden或display: none:
1. 利用aria-hidden与动态tabindex控制
核心思路是当Backdrop激活时,给背景内容的根容器加上aria-hidden="true"(告诉屏幕阅读器忽略这些元素),同时把所有可交互元素的tabindex设为-1(让键盘无法聚焦到它们),关闭Backdrop时再恢复原状。
示例代码:
import { useState } from 'react'; import Backdrop from '@mui/material/Backdrop'; import Button from '@mui/material/Button'; export default function BackdropAccessibility() { const [open, setOpen] = useState(false); const handleToggle = () => { setOpen(!open); }; return ( <div> {/* 背景内容容器:根据Backdrop状态设置aria-hidden */} <div aria-hidden={open} className="background-content"> <Button variant="contained" tabIndex={open ? -1 : 0}> 背景按钮 </Button> <input type="text" placeholder="背景输入框" tabIndex={open ? -1 : 0} /> </div> <Backdrop sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }} open={open} onClick={handleToggle} > {/* Backdrop内的内容,保持可访问 */} <Button variant="contained" onClick={handleToggle}> 关闭遮罩 </Button> </Backdrop> <Button variant="outlined" onClick={handleToggle}> 打开遮罩 </Button> </div> ); }
如果背景里的可交互元素很多,手动设置tabindex太麻烦,可以写个自定义Hook或者用Ref批量处理元素的tabindex属性。
2. 结合Material UI Modal的底层逻辑
其实Material UI的Modal组件本身就自带了“隔离背景交互”的功能,它会自动给背景元素添加aria-hidden,并管理焦点。如果你只是需要遮罩层+背景隔离,可以直接用Modal包裹内容,或者把Backdrop和Modal结合使用:
import { useState } from 'react'; import Modal from '@mui/material/Modal'; import Backdrop from '@mui/material/Backdrop'; import Button from '@mui/material/Button'; export default function ModalWithBackdrop() { const [open, setOpen] = useState(false); const handleToggle = () => { setOpen(!open); }; return ( <div> <Button variant="outlined" onClick={handleToggle}> 打开带隔离的遮罩 </Button> <Modal open={open} onClose={handleToggle} aria-labelledby="modal-modal-title" aria-describedby="modal-modal-description" // 如果你不需要滚动锁定,可以开启这个属性 // disableScrollLock={true} > <Backdrop sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.modal + 1 }} open={open} > <Button variant="contained" onClick={handleToggle}> 关闭遮罩 </Button> </Backdrop> </Modal> {/* 背景内容会被Modal自动隔离 */} <div className="background-content"> <Button variant="contained">背景按钮</Button> <input type="text" placeholder="背景输入框" /> </div> </div> ); }
这种方法最省心,因为Modal已经帮你处理了所有无障碍相关的细节,不用自己手动管理属性。
3. 自定义焦点陷阱(Focus Trap)
如果需要更精细的控制,比如只允许焦点在Backdrop内的元素间循环,可以用焦点陷阱库(比如focus-trap-react),或者自己实现简单的焦点管理:
import { useState, useRef, useEffect } from 'react'; import Backdrop from '@mui/material/Backdrop'; import Button from '@mui/material/Button'; import focusTrap from 'focus-trap'; export default function FocusTrapBackdrop() { const [open, setOpen] = useState(false); const backdropRef = useRef(null); let trap = null; useEffect(() => { if (open) { // 创建焦点陷阱,限制焦点在Backdrop内 trap = focusTrap(backdropRef.current, { onDeactivate: () => setOpen(false), }); trap.activate(); } else { if (trap) trap.deactivate(); } return () => { if (trap) trap.deactivate(); }; }, [open]); return ( <div> <Button variant="outlined" onClick={() => setOpen(true)}> 打开带焦点陷阱的遮罩 </Button> <Backdrop ref={backdropRef} sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }} open={open} > <Button variant="contained" onClick={() => setOpen(false)}> 关闭遮罩 </Button> <Button variant="outlined">遮罩内按钮2</Button> </Backdrop> {/* 背景元素无法获取焦点 */} <div className="background-content"> <Button variant="contained">背景按钮</Button> <input type="text" placeholder="背景输入框" /> </div> </div> ); }
这个方法不仅能阻止背景元素被访问,还能提升键盘导航的体验,让用户在遮罩打开时只能在遮罩内操作。
备注:内容来源于stack exchange,提问作者hokwanhung
相关产品推荐
相关产品推荐

