1

I have the following rather simple React Class:

import React from "react"

export default class DoubleClick extends React.Component {  

  constructor(props) {
    super(props);
    this.state = {
    };
    this.handleClick = this.handleClick.bind(this);
    this.handleDoubleClick = this.handleDoubleClick.bind(this);
  }
  handleClick() {
    console.log("Click");
  }
  handleDoubleClick() {
    console.log("Double Click");
  }
  render() {
    return (
      <div style={{backgroundColor: 'pink'}}>
          <div onClick={this.handleClick}> 
            <span onDoubleClick={this.handleDoubleClick}> Hello </span> 
            <span onDoubleClick={this.handleDoubleClick}> world. </span>
          </div>
      </div>
    );
  }
} 

When somebody single-clicks on the outer div, I want to call handleClick, when somebody double-clicks on any of the inner spans I want to call handleDoubleClick, but not the handleClick for the outer div as well.

However, whenever I double-click, handleDoubleClick() is called, but handleClick is also called, namely twice.

I would like to call handleClick() only when I click, but do not double-click - is this possible?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
R. Kohlisch
  • 2,823
  • 6
  • 29
  • 59
  • Possible duplicate of [onClick works but onDoubleClick is ignored on React component](https://stackoverflow.com/questions/25777826/onclick-works-but-ondoubleclick-is-ignored-on-react-component) – Dez Feb 16 '18 at 20:16
  • It's not ignored in my case though. – R. Kohlisch Feb 16 '18 at 20:17
  • But the accepted answer is what you need to know. Not being able to differentiate single from double click in the same DOM component is a DOM limitation: https://stackoverflow.com/a/25780754/305953 – Dez Feb 16 '18 at 20:18
  • So it's just not possible to differentiate the two? – R. Kohlisch Feb 16 '18 at 20:20
  • It is not possible to do reliably. It is possible to implement heuristics to come close with some important limitations such as sacrificing accessibility. As such it wouldn't be advisable, instead try to rearrange your elements so their events don't clash. – Nic Nilov Feb 16 '18 at 20:28
  • Thanks. Why is it that it cannot be reliably done? Could I not put some checks into place, so that double-clicks and clicks are being recognised? (e.g. measure time differences etc.?) - Why would that not work reliably? Where are the problems? – R. Kohlisch Feb 16 '18 at 20:47
  • Possible duplicate of [Prevent click event from firing when dblclick event fires](https://stackoverflow.com/questions/880608/prevent-click-event-from-firing-when-dblclick-event-fires) – gforce301 Feb 16 '18 at 21:24
  • There are so many answers to this that I can't believe this question hasn't been closed yet. Why bother to search when you can just ask? – gforce301 Feb 16 '18 at 21:31

2 Answers2

5

I have a HOC that I use to approximate what you're looking for:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

export default class DoubleClick extends Component {
  constructor(props) {
    super(props);
    this.onClick = this.onClick.bind(this);
    this.onDoubleClick = this.onDoubleClick.bind(this);
    this.timeout = null;
  }

  onClick(e) {
    e.preventDefault();

    if(this.timeout === null) {
      this.timeout = window.setTimeout(() => {
        this.timeout = null;
        this.props.onClick();
      }, 300);
    }
  }

  onDoubleClick(e) {
    e.preventDefault();
    window.clearTimeout(this.timeout);
    this.timeout = null;
    this.props.onDoubleClick();
  }

  render() {
    const { onClick, onDoubleClick, children, ...childProps } = this.props;
    const props = Object.assign(childProps, { onClick: this.onClick, onDoubleClick: this.onDoubleClick });
    return React.cloneElement(children, props);
  }
}

DoubleClick.propTypes = {
  onClick: PropTypes.func.isRequired,
  onDoubleClick: PropTypes.func.isRequired,
  children: PropTypes.element.isRequired,
};

Used like this:

<DoubleClick onClick={clickHandler} onDoubleClick={doubleClickHandler}>
  <button>Click or double click me</button>
</DoubleClick>

When the first click is received, it sets a 300ms timeout. If no second click is received within 300ms, the function in the onClick prop will be invoked. If a second click is received within 300ms, the function in the onDoubleClick prop will be invoked, and the timeout is canceled so the onClick does not fire.

Needless to say, this is an imperfect approximation, but I've found it to be a sufficiently satisfying user experience in practice.

Zach Schneider
  • 13,317
  • 1
  • 14
  • 6
0

Like others said, it's not possible to do a click event on the parent with a doubleClick on the children without a timeout (I think).

If you can sacrifice the UX part of the 500ms until the click is recognized as a non-doubleclick you can work with timeout.

constructor(props) {
    super(props);
    this.state = {
    };
    this.handleClick = this.handleClick.bind(this);
    this.handleDoubleClick = this.handleDoubleClick.bind(this);

    // hack doubleclick
    this.doubleClickTimeout = 500; // default doubleclick timeout
    this.clickedTimeout = null;
  }
  handleClick(ev) {
    if (!this.clickedTimeout) {
      this.clickedTimeout = setTimeout(() => {
        this.clickedTimeout = null;
        // do your stuff here
        console.log('Clicked');
      }, this.doubleClickTimeout);
    }


  }
  handleDoubleClick() {
    clearTimeout(this.clickedTimeout);
    this.clickedTimeout = null;
    console.log("Double Click");

  }
  render() {
    return (
      <div style={{backgroundColor: 'pink'}}>
          <div onClick={this.handleClick}>
            <span onDoubleClick={this.handleDoubleClick}> Hello </span>
            <span onDoubleClick={this.handleDoubleClick}> world. </span>
          </div>
      </div>
    );
  }
eXcoo
  • 59
  • 4