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

时间:2014-09-11 01:57:06

标签: 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'));

8 个答案:

答案 0 :(得分:46)

这不是React的限制,它是DOM clickdblclick事件的限制。正如Quirksmode的click documentation所建议的那样:

  

不要在同一元素上注册click和dblclick事件:无法区分单击事件和导致dblclick事件的点击事件。

有关更新的文档,dblclick event上的W3C规范指出:

  

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

双击事件必然会在两次点击事件后发生。

修改

另一个建议的阅读是jQuery&#39; dblclick handler

  

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

答案 1 :(得分:14)

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

  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>

答案 2 :(得分: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;

2。 lodash debounce

我有另一个使用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;
肥皂盒上的

我很欣赏双击并不容易被检测到的想法,但无论好坏, IS 存在的范例和用户理解的范例,因为它在操作系统中很普遍。此外,它是现代浏览器仍然支持的范例。在从DOM规范中删除它之前,我的观点是React应该支持onDoubleClick支持onClick// controller public ActionResult Index() { model = new SampleViewModel(); model.Populate(database); return View(model); } // view model public class SampleViewModel { public SampleViewModel(DbContext db) { _list1 = context.Db.SqlQuery<SelectList1>("SELECT Id, Value FROM dbo.Table1").ToList(); _list2 = context.Db.SqlQuery<SelectList2>("SELECT Id, Value FROM dbo.Table2").ToList(); _list3 = context.Db.SqlQuery<SelectList3>("SELECT Id, Value FROM dbo.Table3").ToList(); _list4 = context.Db.SqlQuery<SelectList4>("SELECT Id, Value FROM dbo.Table4").ToList(); _list5 = context.Db.SqlQuery<SelectList5>("SELECT Id, Value FROM dbo.Table5").ToList(); } private readonly List<SelectList1> _list1; public int SelectedList1Id { get; set; } public IEnumerable<SelectListItem> List1 { get { return new SelectList(_list1, "Id", "Value");} } -//- _list2 -//- _list3 -//- _list4 -//- _list5 } 。不幸的是,他们似乎没有。

答案 3 :(得分:4)

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

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

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

        // click in a delay < 250 = double click detected
        if (click === 2) actionDoubleClick();

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

    return () => {
        setClick(prev => prev + 1);
    };
}

然后可以在组件中使用:

const click = useSimpleAndDoubleClick(callbackClick, callbackDoubleClick);
<button onClick={click}>clic</button>

答案 4 :(得分:3)

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

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>;

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

click 
double click 
triple click 

现场演示

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

答案 5 :(得分:1)

这就是我所做的。欢迎提出任何改进建议。

class DoubleClick extends React.Component {
  state = {counter: 0}

  handleClick = () => {
   this.setState(state => ({
    counter: this.state.counter + 1,
  }))
 }


  handleDoubleClick = () => {
   this.setState(state => ({
    counter: this.state.counter - 2,
  }))
 }

 render() {
   return(
   <>
    <button onClick={this.handleClick} onDoubleClick={this.handleDoubleClick>
      {this.state.counter}
    </button>
   </>
  )
 }
}

答案 6 :(得分:0)

这是基于Erminea解决方案的具有增量和识别值的like按钮的解决方案。

useEffect(() => {
    let singleClickTimer;
    if (clicks === 1) {
      singleClickTimer = setTimeout(
        () => {
          handleClick();
          setClicks(0);
        }, 250);
    } else if (clicks === 2) {
      handleDoubleClick();
      setClicks(0);
    }
    return () => clearTimeout(singleClickTimer);
  }, [clicks]);

  const handleClick = () => {
    console.log('single click');
    total = totalClicks + 1;
    setTotalClicks(total);
  }

  const handleDoubleClick = () => {
    console.log('double click');
    if (total > 0) {
      total = totalClicks - 1;
    }
    setTotalClicks(total);
  }

  return (
    <div
      className="likeButton"
      onClick={() => setClicks(clicks + 1)}
    >
      Likes | {totalClicks}
    </div>
)

答案 7 :(得分:0)

这是通过 Promise 实现相同目标的一种方法。 waitForDoubleClick 返回一个 Promise,只有在没有执行双击时才会解析。否则会拒绝。时间可以调整。

    async waitForDoubleClick() {
    return new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        if (!this.state.prevent) {
          resolve(true);
        } else {
          reject(false);
        }
      }, 250);
      this.setState({ ...this.state, timeout, prevent: false })
    });
  }
  clearWaitForDoubleClick() {
    clearTimeout(this.state.timeout);
    this.setState({
      prevent: true
    });
  }
  async onMouseUp() {
    try {
      const wait = await this.waitForDoubleClick();
      // Code for sinlge click goes here.
    } catch (error) {
      // Single click was prevented.
      console.log(error)
    }
  }