onClick有效但在React组件上忽略onDoubleClick

thi*_*bus 54 javascript reactjs

我正在使用React构建一个Minesweeper游戏,并希望在单击或双击单元格时执行不同的操作.目前,该onDoubleClick功能永远不会触发,显示警报onClick.如果我删除onClick处理程序,onDoubleClick工作.为什么两个事件都不起作用?是否可以在元素上同时包含两个事件?

/** @jsx React.DOM */

var Mine = React.createClass({
  render: function(){
    return (
      <div className="mineBox" id={this.props.id} onDoubleClick={this.props.onDoubleClick} onClick={this.props.onClick}></div>
    )
  }
});

var MineRow = React.createClass({
  render: function(){
    var width = this.props.width,
        row = [];
    for (var i = 0; i < width; i++){
      row.push(<Mine id={String(this.props.row + i)} boxClass={this.props.boxClass} onDoubleClick={this.props.onDoubleClick} onClick={this.props.onClick}/>)
    }
    return (
      <div>{row}</div>
    )
  }
})

var MineSweeper = React.createClass({
  handleDoubleClick: function(){
    alert('Double Clicked');
  },
  handleClick: function(){
    alert('Single Clicked');
  },
  render: function(){
    var height = this.props.height,
        table = [];
    for (var i = 0; i < height; i++){
      table.push(<MineRow width={this.props.width} row={String.fromCharCode(97 + i)} onDoubleClick={this.handleDoubleClick} onClick={this.handleClick}/>)
    }
    return (
      <div>{table}</div>
    )
  }
})

var bombs = ['a0', 'b1', 'c2'];
React.renderComponent(<MineSweeper height={5} width={5} bombs={bombs}/>, document.getElementById('content'));
Run Code Online (Sandbox Code Playgroud)

Ros*_*len 51

这不是React的限制,它是DOM clickdblclick事件的限制.正如Quirksmode的点击文档所示:

不要在同一元素上注册click和dblclick事件:不可能将单击事件与导致dblclick事件的单击事件区分开来.

有关更新的文档,该dblclick事件的W3C规范指出:

当在元素上单击指针设备的主按钮两次时,用户代理必须调度此事件.

两次单击事件后必然会发生双击事件.

编辑:

另一个建议读取是jQuery的dblclick处理程序:

将处理程序绑定到同一元素的click和dblclick事件是不可取的.触发的事件序列因浏览器而异,有些事件在dblclick之前接收到两个点击事件,而其他事件只接收一个事件.双击敏感度(双击检测到的点击之间的最长时间)可能因操作系统和浏览器而异,通常可由用户配置.

  • 愚蠢至极的评论。从 UI 设计的角度来看,不建议为没有单击处理程序的元素提供 dbl-click 处理程序(否则只需使用 click)。在任何可以想象的使用 dblClick 的 UI 约定下,不得吃掉 dblClick 之前的点击(通常执行选择)。jQuery 注释只是稍微有用一点;需要注意的是,在某些平台上,可能会收到 click/dblClick,而在其他平台上可能会收到 click/click/DblClick。(它实际上是 React 的一个限制——重新渲染选择状态会杀死 dblClick)。 (4认同)

Aka*_*edi 17

通过在触发正常点击动作时提供非常小的延迟可以实现所需的结果,当双击事件发生时将被取消.

  let timer = 0;
  let delay = 200;
  let prevent = false;

  doClickAction() {
    console.log(' click');
  }
  doDoubleClickAction() {
    console.log('Double Click')
  }
  handleClick() {
    let me = this;
    timer = setTimeout(function() {
      if (!prevent) {
        me.doClickAction();
      }
      prevent = false;
    }, delay);
  }
  handleDoubleClick(){
    clearTimeout(timer);
    prevent = true;
    this.doDoubleClickAction();
  }
 < button onClick={this.handleClick.bind(this)} 
    onDoubleClick = {this.handleDoubleClick.bind(this)} > click me </button>
Run Code Online (Sandbox Code Playgroud)

  • 很好的解决方案,谢谢!我只是有点困惑:实际上有必要同时使用“prevent”和“clearTimeout()”吗?对于这种情况,任何一个都不够吗? (2认同)

Erm*_*Nea 17

您可以使用自定义钩子来处理简单的单击和双击,如下所示:

import { useState, useEffect } from 'react';

function useSingleAndDoubleClick(actionSimpleClick, actionDoubleClick, delay = 250) {
    const [click, setClick] = useState(0);

    useEffect(() => {
        const timer = setTimeout(() => {
            // simple click
            if (click === 1) actionSimpleClick();
            setClick(0);
        }, delay);

        // the duration between this click and the previous one
        // is less than the value of delay = double-click
        if (click === 2) actionDoubleClick();

        return () => clearTimeout(timer);
        
    }, [click]);

    return () => setClick(prev => prev + 1);
}
Run Code Online (Sandbox Code Playgroud)

然后在您的组件中,您可以使用:

const click = useSingleAndDoubleClick(callbackClick, callbackDoubleClick);
<button onClick={click}>clic</button>
Run Code Online (Sandbox Code Playgroud)

  • 这绝对是 2020 年的正确答案:) (2认同)
  • 很好的解决方案,为了使其完美,我建议将其重命名为 useSingleAndDoubleClick 谢谢;) (2认同)

Jef*_*ley 13

编辑:

我发现这不是React 0.15.3的问题.


原版的:

对于React 0.13.3,这里有两个解决方案.

1. ref回调

请注意,即使在双击的情况下,单击处理程序也会被调用两次(每次单击一次).

const ListItem = React.createClass({

  handleClick() {
    console.log('single click');
  },

  handleDoubleClick() {
    console.log('double click');
  },

  refCallback(item) {
    if (item) {
      item.getDOMNode().ondblclick = this.handleDoubleClick;
    }
  },

  render() {
    return (
      <div onClick={this.handleClick}
           ref={this.refCallback}>
      </div>
    );
  }
});

module.exports = ListItem;
Run Code Online (Sandbox Code Playgroud)

2. lodash去抖

我有另一个使用的解决方案lodash,但由于复杂性我放弃了它.这样做的好处是"点击"只被调用一次,而在"双击"的情况下则完全没有.

import _ from 'lodash'

const ListItem = React.createClass({

  handleClick(e) {
    if (!this._delayedClick) {
      this._delayedClick = _.debounce(this.doClick, 500);
    }
    if (this.clickedOnce) {
      this._delayedClick.cancel();
      this.clickedOnce = false;
      console.log('double click');
    } else {
      this._delayedClick(e);
      this.clickedOnce = true;
    }
  },

  doClick(e) {
    this.clickedOnce = undefined;
    console.log('single click');
  },

  render() {
    return (
      <div onClick={this.handleClick}>
      </div>
    );
  }
});

module.exports = ListItem;
Run Code Online (Sandbox Code Playgroud)

在肥皂盒上

我很欣赏的想法,双击是不容易发现的东西,但是是好还是坏它IS存在用户理解,因为它在操作系统中流行的范式和一个.此外,它是现代浏览器仍然支持的范例.直到将它从DOM规范中删除为止,我认为React应该支持一个功能onDoubleClick正确的支柱onClick.不幸的是,他们似乎没有.

  • @Sassan我没有在最新版本的react(15.6.1)上看到这个问题 - 两者都按预期开火.请注意,双击时,普通的点击处理程序会激发两次,但我很确定这是预期的. (2认同)

Nea*_*arl 5

ondoubleclick您可以使用event.detail来获取当前点击次数,而不是使用。它是鼠标在短时间内在同一区域被点击的次数。

const handleClick = (e) => {
  switch (e.detail) {
    case 1:
      console.log("click");
      break;
    case 2:
      console.log("double click");
      break;
    case 3:
      console.log("triple click");
      break;
  }
};

return <button onClick={handleClick}>Click me</button>;
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,如果您三次单击按钮,它将打印所有 3 个案例:

click 
double click 
triple click 
Run Code Online (Sandbox Code Playgroud)

现场演示

编辑 25777826/onclick-works-but-ondoubleclick-is-ignored-on-react-component

  • 这是一个很好且简单的解决方案。但是,在双击时,它会同时触发单击和双击,对于三次单击,依此类推。幸运的是,对于我当前的用例来说这不是问题:) (3认同)
  • 最佳答案!即使元素被重新渲染以响应初始单击事件,也可以正常工作!我推荐这个解决方案。我的问题:点击选择元素;React 重新渲染原生元素,以便在大型虚拟网格中显示选择状态;就新创建的本机元素而言(在 Chrome 上),第二次单击不是双击。但这种方法即使在进行繁重的渲染时也能工作,并且似乎可以抵抗渲染时间带来的滞后问题(计时器则不然)。 (3认同)
  • 如果您只想双击,则可以检查是否“e.detail === 2”,这样做将过滤单击和三次单击@DanV (2认同)
  • 这是一个很好的解决方案,比编写自定义代码来检查计时器要好得多。双击延迟可以在用户的​​操作系统中自定义,并且浏览器知道如何使用该值。您的前端代码不应硬编码延迟值。 (2认同)