You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中event.stopPropagation()无法区分鼠标与触摸事件的问题

解决React导航菜单触屏点击展开不跳转、桌面点击跳转的问题

这个问题我之前做移动端导航时也碰到过,核心原因是触屏设备中,浏览器会在touchstart/touchend事件触发后约300ms自动触发click事件(为了兼容双击缩放功能),单纯用event.stopPropagation()是阻止不了这个后续click的——它不是通过冒泡产生的,是浏览器主动触发的。下面给你几个针对性的解决方案:

方案一:用状态标记触屏操作,拦截后续click

通过添加一个状态变量标记当前是否是触屏发起的交互,在click事件中判断是否执行跳转逻辑:

  1. 首先在组件初始化状态中添加标记:
constructor(props) {
  super(props);
  this.state = {
    hover: false,
    isTouchInitiated: false // 标记是否是触屏操作
  };
  this.timeout = null;
}
  1. 修改handleTouch方法,阻止默认行为并标记状态:
handleTouch = (event) => {
  console.log("handleTouch");
  event.preventDefault(); // 阻止浏览器默认行为,包括后续自动触发的click
  clearTimeout(this.timeout);
  
  this.setState({ 
    hover: true,
    isTouchInitiated: true 
  });
}
  1. 修改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);
  }
}
  1. 调整渲染结构,简化事件绑定层级(避免嵌套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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 18:42:27