MUI Tooltip点击事件:无法聚焦关闭按钮且弹窗自动闪关问题
问题描述
基于MUI的Tooltip组件自定义了TooltipComponent,功能类似Dialog:点击显示“CLICK”的div后,弹出包含文本和“x”关闭按钮的内容,点击“x”可关闭弹窗。为触发元素添加了onClick和onKeyPress事件,二者作用均为打开弹窗。
出于无障碍需求,要求弹窗打开时将焦点设置到“x”按钮,通过React的ref实现此功能。但onKeyPress事件下功能正常,onClick事件不仅无法设置焦点,弹窗还会自动打开后立即关闭(按代码逻辑仅点击“x”才可关闭)。
问题原因
这是事件冒泡+MUI ClickAwayListener的判定逻辑共同导致的:
- 点击触发div时,
toggleTooltip执行将open设为true,Tooltip渲染弹窗内容。但点击事件会向上冒泡,被内部的ClickAwayListener捕获——此时弹窗刚渲染完成,触发元素的点击事件会被判定为“弹窗外部点击”,从而调用closeTooltip关闭弹窗。 - 键盘事件(
onKeyPress)不会触发ClickAwayListener的“点击外部”判定逻辑,因此能正常打开弹窗并设置焦点。 - 弹窗被立即关闭,导致
useEffect中设置焦点的代码还未执行就失效。
解决方法
核心修改点
- 阻止触发元素的点击事件冒泡:避免
ClickAwayListener误将触发点击判定为外部点击 - 优化焦点设置逻辑:确保弹窗稳定打开后再执行焦点设置
修改后的代码
import React, { useEffect, useRef, useState } from 'react'; import { Tooltip, ClickAwayListener } from '@mui/material'; function TooltipComponent() { const [open, setOpen] = useState(false); const toggleTooltip = (e) => { // 阻止点击事件冒泡,避免被ClickAwayListener捕获 e.stopPropagation(); setOpen(!open); }; const closeTooltip = () => setOpen(false); const closeBtnRef = useRef(null); useEffect(() => { if (open && closeBtnRef.current) { // 确保弹窗打开后再设置焦点 closeBtnRef.current.focus(); } }, [open]); const TooltipContent = ( <ClickAwayListener onClickAway={closeTooltip}> <div style={{ position: 'relative' }}> <div tabIndex={0} ref={closeBtnRef} onClick={closeTooltip} onKeyPress={(e) => { // 仅在按下回车或空格时关闭 if (e.key === 'Enter' || e.key === ' ') { closeTooltip(); } }} aria-label="close tooltip" // 替换非标准属性为无障碍规范属性 style={{ position: 'absolute', left: '-3px', top: '-3px', fontSize: '36px', fontWeight: 'light', cursor: 'pointer', }} > x </div> <div aria-label="tooltip content"> <div style={{ padding: '24px', color: 'black' }}> Top - Start -{' '} <div> <a href="#">I am a link</a> </div> </div> </div> </div> </ClickAwayListener> ); return ( <Tooltip title={TooltipContent} arrow disableHoverListener={true} open={open} placement="bottom" onClose={closeTooltip} PopperProps={{ disablePortal: true, role: 'dialog', 'aria-live': 'polite', }} > <div onClick={toggleTooltip} onKeyPress={(e) => { // 仅在按下回车或空格时触发打开 if (e.key === 'Enter' || e.key === ' ') { toggleTooltip(e); } }} tabIndex={0} // 给触发元素添加tabIndex,确保键盘可访问 style={{ display: 'inline-block', textAlign: 'center', cursor: 'pointer', }} > <div style={{ margin: '6px', color: 'black' }}> CLICK </div> </div> </Tooltip> ); } export default TooltipComponent;
额外优化说明
- 替换了非标准的
aria-description为aria-label,符合WAI-ARIA无障碍规范 - 给触发元素添加
tabIndex={0},确保键盘用户可通过Tab键聚焦触发元素 - 优化键盘事件逻辑,仅在按下回车或空格时执行打开/关闭操作,匹配原生交互习惯
内容的提问来源于stack exchange,提问作者yakob
相关产品推荐
相关产品推荐

