HeadlessUI React如何检测当前聚焦元素解决快捷键冲突问题
方案1:利用HeadlessUI内置状态判断(推荐)
HeadlessUI的Menu组件本身支持受控模式,通过将Menu的开放状态提升并对外暴露,可以完全避免依赖DOM属性判断,逻辑最稳定:
- 改造
ProjectSelector组件,通过useImperativeHandle暴露Menu状态和触发方法:
export const ProjectSelector = forwardRef((props, ref) => { const [isMenuOpen, setIsMenuOpen] = useState(false) useImperativeHandle(ref, () => ({ triggerMenu: () => selectorBtnRef.current?.click(), isMenuOpen: isMenuOpen })) const selectorBtnRef = useRef(null) return ( <Menu open={isMenuOpen} onOpenChange={setIsMenuOpen}> <Menu.Button ref={selectorBtnRef} as="div"> {/* 按钮内容 */} </Menu.Button> <Menu.Items> {/* 搜索输入框、菜单项内容 */} </Menu.Items> </Menu> ) })
- 调整快捷键监听逻辑,仅在菜单未打开时触发:
useWindowEvent("keydown", (event) => { if (event.key === "p" && !selectorRef.current?.isMenuOpen) { event.preventDefault() event.stopPropagation() selectorRef.current?.triggerMenu() } })
方案2:优化DOM判断逻辑(无侵入改造成本低)
如果不想调整现有组件结构,可以优化原有的DOM判断逻辑,覆盖所有可输入场景和菜单内场景,鲁棒性更高:
useWindowEvent("keydown", (event) => { const target = event.target // 排除可输入元素场景:输入框、文本域、可编辑富文本 const isInputScene = target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable // 排除焦点在菜单内部的场景 const isInMenu = target.closest('[role="menu"]') !== null if (event.key === "p" && !isInputScene && !isInMenu) { event.preventDefault() event.stopPropagation() selectorRef.current?.click() } })
方案3:检测Dialog激活状态(匹配你的原始需求)
HeadlessUI的Dialog打开时会自带data-headlessui-state="open"属性,且焦点陷阱会把焦点限制在Dialog内部,可以通过该属性判断Dialog是否处于激活状态:
useWindowEvent("keydown", (event) => { // 查找当前激活的Dialog节点 const activeDialog = document.querySelector('[role="dialog"][data-headlessui-state="open"]') // 无激活Dialog 或 当前焦点不在激活Dialog内时不触发 if (!activeDialog || !activeDialog.contains(document.activeElement)) return const target = event.target const isInputScene = target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable if (event.key === "p" && !isInputScene) { event.preventDefault() event.stopPropagation() selectorRef.current?.click() } })
内容的提问来源于stack exchange,提问作者Oscar Franco
相关产品推荐
相关产品推荐

