触摸设备下按钮onTouch事件与鼠标事件冲突及preventDefault报错求助
解决触屏设备点击按钮触发
onMouseOver的冲突问题 你遇到的是触屏设备的触摸事件模拟鼠标事件机制导致的问题:浏览器为了兼容桌面端代码,在触发touchstart/touchend后,会自动模拟触发mouseover/mousemove/click等鼠标事件;而React的触摸事件默认是**被动监听(passive)**的,所以无法直接调用preventDefault()阻止模拟行为,否则会抛出你看到的错误。
下面是几个实用的解决思路:
方法1:用标记区分触摸/鼠标操作
通过useRef存储触摸状态,在鼠标事件中跳过触摸触发的模拟事件:
import { useState, useRef } from 'react'; function StartButton() { const [isShowDescription, setIsShowDescription] = useState(false); const isTouchTriggered = useRef(false); const displayDescription = () => { setIsShowDescription(true); }; const notDisplayDescription = () => { setIsShowDescription(false); }; const handleTouchStart = () => { isTouchTriggered.current = true; displayDescription(); }; const handleTouchEnd = () => { isTouchTriggered.current = false; notDisplayDescription(); }; const handleMouseOver = () => { // 仅处理非触摸触发的鼠标悬停事件 if (!isTouchTriggered.current) { displayDescription(); } }; return ( <button className='start-button' onMouseOver={handleMouseOver} onMouseOut={notDisplayDescription} onTouchStart={handleTouchStart} onTouchEnd={handleTouchEnd} > Click here to start </button> ); }
核心逻辑是:触摸操作触发时标记状态,后续的模拟鼠标事件会被直接跳过,避免重复更新状态。
方法2:通过媒体查询区分设备类型
利用CSS媒体查询@media (hover: hover)判断设备是否支持原生hover行为(桌面端一般支持,触屏设备大多不支持),针对性绑定事件:
import { useState, useEffect, useRef } from 'react'; function StartButton() { const [isShowDescription, setIsShowDescription] = useState(false); const buttonRef = useRef(null); const displayDescription = () => setIsShowDescription(true); const notDisplayDescription = () => setIsShowDescription(false); useEffect(() => { const button = buttonRef.current; if (!button) return; // 检测设备是否支持原生hover const supportsHover = window.matchMedia('(hover: hover)').matches; if (supportsHover) { // 桌面端:绑定鼠标事件 button.addEventListener('mouseover', displayDescription); button.addEventListener('mouseout', notDisplayDescription); } else { // 触屏端:绑定触摸事件,保持passive:true符合性能要求 button.addEventListener('touchstart', displayDescription, { passive: true }); button.addEventListener('touchend', notDisplayDescription, { passive: true }); } // 组件卸载时清理事件监听 return () => { if (supportsHover) { button.removeEventListener('mouseover', displayDescription); button.removeEventListener('mouseout', notDisplayDescription); } else { button.removeEventListener('touchstart', displayDescription); button.removeEventListener('touchend', notDisplayDescription); } }; }, []); return ( <button ref={buttonRef} className='start-button'> Click here to start </button> ); }
这个方法从根源避免冲突:触屏设备不会绑定鼠标事件,自然不会触发mouseover;同时触摸事件保持passive: true,符合浏览器性能优化标准。
方法3:手动设置非被动触摸事件监听器(不推荐)
如果必须用preventDefault()阻止鼠标事件模拟,可以通过ref手动添加触摸事件监听器并设置passive: false:
import { useState, useRef, useEffect } from 'react'; function StartButton() { const [isShowDescription, setIsShowDescription] = useState(false); const buttonRef = useRef(null); const displayDescription = () => setIsShowDescription(true); const notDisplayDescription = () => setIsShowDescription(false); useEffect(() => { const button = buttonRef.current; if (!button) return; const handleTouchStart = (e) => { e.preventDefault(); // 阻止触摸后的鼠标事件模拟 displayDescription(); }; button.addEventListener('touchstart', handleTouchStart, { passive: false }); button.addEventListener('touchend', notDisplayDescription); button.addEventListener('mouseover', displayDescription); button.addEventListener('mouseout', notDisplayDescription); return () => { button.removeEventListener('touchstart', handleTouchStart); button.removeEventListener('touchend', notDisplayDescription); button.removeEventListener('mouseover', displayDescription); button.removeEventListener('mouseout', notDisplayDescription); }; }, []); return ( <button ref={buttonRef} className='start-button'> Click here to start </button> ); }
⚠️ 注意:此方法可能干扰触屏设备的默认行为(如滚动、缩放),且不符合浏览器性能优化建议,优先选择前两种方案。
内容的提问来源于stack exchange,提问作者MoranLev
相关产品推荐
相关产品推荐

