React的onClick和onTouchStart同时触发了怎么办?

18

我在我的React应用程序中有一个div,我需要处理单击和触摸事件。但是当我在移动设备上轻敲时,它会触发两个事件。

如果我在移动设备上滑动或者在普通浏览器上单击,它都正常工作,每种情况下只触发一个事件。

如何解决这个问题,使得在轻敲时不会同时触发两个事件?

<div
  className={myClasses}
  onClick={this.myHandle}
  onTouchStart={this.myHandle}
  >&nbsp;
</div>

1
为什么需要同时使用TouchStart和click?click将在移动设备和桌面设备上都被调用。 生命周期通常是:touchstart(mousedown)-> touchend(mouseup)-> click。 因此,除非您需要对touchstart执行显式操作,否则可以将其删除。 - PiniH
我明白你的意思。我之前只有一个点击事件,但是如果用户滑动,它不会触发点击事件,只会触发触摸事件。这有点像旋转木马,用户可以点击或滑动“下一张卡片”来进行滑动。 - Metarat
1
我的所有控件经验告诉我,它们不会同时触发。你确定你正确分析了这种情况吗?event.preventDefault() 对你有帮助吗? - Shammoo
@Shammoo 我的意思是它们都是通过同一鼠标操作触发的。当我轻触时,两个事件都会被触发,首先是 onTouchStart,然后是 onClick。我的处理程序 this.myHandle 更改了状态,因此组件再次呈现。第二个事件(点击)仍然会被触发。 - Metarat
1
对于未来发现此问题的人:这就是它应该的行为。onClick 不是一个“鼠标”事件,而是一个“点击”事件,在触摸设备上相当于“touchStart-followed-by-touchEnd”。因此,在触摸设备上,touchstart 触发,调用处理程序,touchend 触发,没有处理程序,然后 click 触发,再次调用处理程序。只需删除 onTouchStart 即可完成。 - Mike 'Pomax' Kamermans
4个回答

17

事件触发有一个明确定义的顺序(source):

touchstart
Zero or more touchmove events, depending on movement of the finger(s)
touchend
mousemove
mousedown
mouseup
click

如果您想在触摸事件触发之前防止点击事件,您可以在触摸结束的事件处理程序中使用event.preventDefault()来防止点击事件触发。
function App() {
  const handleClick = () => {
    alert('click');
  };

  const handleTouchEnd = (event) => {
    alert('touchend');

    event.preventDefault();
  };

  return (
    <button
      type="button"
      onClick={handleClick}
      onTouchEnd={handleTouchEnd}
    >
      Click Me
    </button>
  );
}

然而,这并非普适。例如,你不能使用 event.preventDefault()mousedown 事件中来 prevent 一个 click 事件。如果你正在寻找解决方案(虽然不确定何时会出现这种情况),你需要使用 ref 来代替:
function App() {
  const prevent = React.useRef(false);

  const handleClick = () => {
    if (!prevent.current) {
      alert('click');
    } else {
      prevent.current = false;
    }
  };

  const handleMouseDown = () => {
    prevent.current = true;

    alert('mousedown');
  };

  return (
    <button
      type="button"
      onClick={handleClick}
      onMouseDown={handleMouseDown}
    >
      Click Me
    </button>
  );
}

2
在React中,我必须执行event.stopPropagation()来阻止它同时触发onTouchStartonMouseDown - cueloop

15

使用触摸事件和鼠标事件之间的类似情况解决了此问题。touchStart/mouseDown 或 touchEnd/mouseUp。根据每种情况,它会触发其中一种。

<div
  className={myClasses}
  onMouseUp={this.myHandle}
  onTouchEnd={this.myHandle}
  >&nbsp;
</div>

你能分享一下this.myHandle函数吗? - cevaris

2
这可能有些晚了,但我找到了一个非常容易实现的解决方案。看起来像这样:
import ReactDom from 'react-dom';

export default class YourClass extends Component {

componentDidMount(){
        ReactDOM.findDOMNode(this).addEventListener('touchstart', (e)=>{ 
            e.preventDefault(); 
            console.log("touchstart triggered");
        });
    }
}

似乎它截取并停止了移动端上所有的onClick调用,这正是我寻找的。

2
为了避免在触摸设备上使用 onClick(),您应该检查页面是否在触摸设备上打开。

要检查它是否在触摸设备上打开:
if (typeof document !== 'undefined') {
  var isTouch = 'ontouchstart' in document.documentElement;
}

然后,将{isTouch ? undefined : this.myHandle}修改为您的鼠标事件:
<div
  className={myClasses}
  onClick={isTouch ? undefined : this.myHandle}
  onTouchStart={this.myHandle}
  >&nbsp;
</div>

这有点棘手,因为一些设备同时处理鼠标和触摸。 - Robo Robok

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接