移动端onClick事件异常求助:游戏网格选中与背景切换异常
问题分析
移动端触摸操作会模拟鼠标事件:点击网格时会触发onMouseEnter,但触摸结束后不会自动触发onMouseLeave(除非触摸离开该元素),导致beingHovered状态一直为true。当再次点击取消选中时,beingHovered || selected的结果仍为true,所以背景无法恢复;而点击其他网格时,当前网格的onMouseLeave会被触发,beingHovered重置为false,背景才正常恢复。
解决方案
方案1:使用Pointer事件替代鼠标事件
Pointer事件是W3C标准,同时兼容鼠标和触摸操作,能自动处理触摸后的状态重置:
function Grid(): JSX.Element { const myRef = useRef<HTMLDivElement>(null); const [beingHovered, setBeingHovered] = useState(false); const [selected, setSelected] = useState(false); const hoverStyle = { backgroundImage: "url(/assets/stamp.png)", backgroundSize: "contain", backgroundRepeat: "no-repeat" }; const background = (beingHovered || selected) ? hoverStyle : {}; return ( <div className="Grid" onPointerEnter={() => setBeingHovered(true)} onPointerLeave={() => setBeingHovered(false)} onClick={() => setSelected(!selected)} style={background} ref={myRef} > <span>The quick brown fox jumps over the lazy dog</span> </div> ); }
方案2:点击时手动重置hover状态
如果需要兼容不支持Pointer事件的环境,可在点击事件中强制重置beingHovered:
function Grid(): JSX.Element { const myRef = useRef<HTMLDivElement>(null); const [beingHovered, setBeingHovered] = useState(false); const [selected, setSelected] = useState(false); const hoverStyle = { backgroundImage: "url(/assets/stamp.png)", backgroundSize: "contain", backgroundRepeat: "no-repeat" }; const background = (beingHovered || selected) ? hoverStyle : {}; const handleClick = () => { setSelected(!selected); setBeingHovered(false); }; return ( <div className="Grid" onMouseEnter={() => setBeingHovered(true)} onMouseLeave={() => setBeingHovered(false)} onClick={handleClick} style={background} ref={myRef} > <span>The quick brown fox jumps over the lazy dog</span> </div> ); }
方案3:用CSS媒体查询分离桌面/移动端效果
将hover样式移到CSS中,仅在桌面端生效,彻底避免触摸事件冲突:
.Grid { /* 基础样式 */ } /* 仅在支持精确指针的设备(桌面端)显示hover效果 */ @media (hover: hover) and (pointer: fine) { .Grid:hover { background-image: url(/assets/stamp.png); background-size: contain; background-repeat: no-repeat; } }
组件代码简化为:
function Grid(): JSX.Element { const myRef = useRef<HTMLDivElement>(null); const [selected, setSelected] = useState(false); const selectedStyle = { backgroundImage: "url(/assets/stamp.png)", backgroundSize: "contain", backgroundRepeat: "no-repeat" }; return ( <div className="Grid" onClick={() => setSelected(!selected)} style={selected ? selectedStyle : {}} ref={myRef} > <span>The quick brown fox jumps over the lazy dog</span> </div> ); }
内容的提问来源于stack exchange,提问作者Nahid Islam Shaiket
相关产品推荐
相关产品推荐

