React中event.stopPropagation()无法区分鼠标与触摸事件的问题
解决React导航菜单触屏点击展开不跳转、桌面点击跳转的问题
这个问题我之前做移动端导航时也碰到过,核心原因是触屏设备中,浏览器会在touchstart/touchend事件触发后约300ms自动触发click事件(为了兼容双击缩放功能),单纯用event.stopPropagation()是阻止不了这个后续click的——它不是通过冒泡产生的,是浏览器主动触发的。下面给你几个针对性的解决方案:
方案一:用状态标记触屏操作,拦截后续click
通过添加一个状态变量标记当前是否是触屏发起的交互,在click事件中判断是否执行跳转逻辑:
- 首先在组件初始化状态中添加标记:
constructor(props) { super(props); this.state = { hover: false, isTouchInitiated: false // 标记是否是触屏操作 }; this.timeout = null; }
- 修改
handleTouch方法,阻止默认行为并标记状态:
handleTouch = (event) => { console.log("handleTouch"); event.preventDefault(); // 阻止浏览器默认行为,包括后续自动触发的click clearTimeout(this.timeout); this.setState({ hover: true, isTouchInitiated: true }); }
- 修改
handleClick方法,根据标记判断是否执行跳转:
handleClick = (event) => { // 如果是触屏触发的后续click,重置标记并返回,不执行跳转 if (this.state.isTouchInitiated) { this.setState({ isTouchInitiated: false }); return; } console.log("handleClick"); // 用react-router的history执行跳转(替代原生a标签href,避免页面刷新) const { config, history } = this.props; if (config.href && config.href !== '/') { history.push(config.href); } }
- 调整渲染结构,简化事件绑定层级(避免嵌套a标签导致的事件混乱):
render() { const config = this.props.config; return ( <Manager> <Target style={{ position: 'relative' }}> <div onClick={this.handleClick} onMouseOver={this.handleClick} onFocus={this.handleClick} onTouchStart={this.handleTouch} > {this.display(config, this.state.hover)} </div> </Target> </Manager> ); }
方案二:利用Pointer Events区分设备类型
如果你的目标浏览器支持Pointer Events(大部分现代浏览器都支持),可以直接通过事件的pointerType判断是触屏还是鼠标操作:
handleClick = (event) => { // 如果是触屏点击(pointerType为'touch'),不执行跳转 if (event.pointerType === 'touch') { return; } // 鼠标/触摸板点击,执行跳转 console.log("handleClick"); const { config, history } = this.props; if (config.href && config.href !== '/') { history.push(config.href); } }
这个方案更简洁,但如果需要兼容旧版浏览器,建议结合方案一使用。
额外优化:点击外部关闭子导航
如果需要实现点击页面其他区域关闭展开的子导航,可以添加文档点击监听:
componentDidMount() { document.addEventListener('click', this.handleDocumentClick); } componentWillUnmount() { document.removeEventListener('click', this.handleDocumentClick); } handleDocumentClick = (event) => { // 判断点击是否在当前组件外部 if (!this.navItemRef.contains(event.target)) { this.setState({ hover: false }); } }
然后在渲染的div上添加ref:
<div ref={ref => this.navItemRef = ref} onClick={this.handleClick} onMouseOver={this.handleClick} onFocus={this.handleClick} onTouchStart={this.handleTouch} >
内容的提问来源于stack exchange,提问作者Alison Williams
相关产品推荐
相关产品推荐

