21

I have a ReactJS component that I want to have different behavior on a single click and on a double click.

I read this question.

<Component
    onClick={this.onSingleClick}
    onDoubleClick={this.onDoubleClick} />

And I tried it myself and it appears as though you cannot register both single click and double click on a ReactJS component.

I'm not sure of a good solution to this problem. I don't want to use a timer because I'm going to have 8 of these single components on my page.

Would it be a good solution to have another inner component inside this one to deal with the double click situation?

Edit:

I tried this approach but it doesn't work in the render function.

render (

    let props = {};

    if (doubleClick) {
        props.onDoubleClick = function
    } else {
        props.onClick = function
    }
    <Component
        {...props} />
);
4
  • Just a suggestion, but double-clicks are easily handled with RxJS gist.github.com/anthonybrown/a5ef9148bd101157d922 Commented Feb 18, 2016 at 20:11
  • The answer you link to already talks about how binding to both the click and dbclick event on a single dom element is inadvisable. Double clicking is not a common user interaction on the web which results it most users not knowing that you can. Using a different interaction method is probably the best approach. Commented Feb 18, 2016 at 21:24
  • @PetersenDidIt yeah I think it's weird too but they want to let the user know what they only clicked one time? There is a double-click setting. Commented Feb 18, 2016 at 21:34
  • I added an answer to the question you linked showing how you can do it. Commented Aug 9, 2016 at 18:40

5 Answers 5

24

Here is the fastest and shortest answer:

CLASS-BASED COMPONENT


class DoubleClick extends React.Component {
    timer = null

    onClickHandler = event => {
        clearTimeout(this.timer);

        if (event.detail === 1) {
            this.timer = setTimeout(this.props.onClick, 200)
        } else if (event.detail === 2) {
            this.props.onDoubleClick()
        }
    }

    render() {
        return (
            <div onClick={this.onClickHandler}>
                {this.props.children}
            </div>
        )
    }
}

FUNCTIONAL COMPONENT


const DoubleClick = ({ onClick = () => { }, onDoubleClick = () => { }, children }) => {
    const timer = useRef()

    const onClickHandler = event => {
        clearTimeout(timer.current);

        if (event.detail === 1) {
            timer.current = setTimeout(onClick, 200)
        } else if (event.detail === 2) {
            onDoubleClick()
        }
    }

    return (
        <div onClick={onClickHandler}>
            {children}
        </div>
    )
}

DEMO

var timer;

function onClick(event) {
  clearTimeout(timer);
  
  if (event.detail === 1) {
    timer = setTimeout(() => {
      console.log("SINGLE CLICK");
    }, 200)

  } else if (event.detail === 2) {
    console.log("DOUBLE CLICK");
  }
}

document.querySelector(".demo").onclick = onClick;
.demo {
  padding: 20px 40px;
  background-color: #eee;
  user-select: none;
}
<div class="demo">
  Click OR Double Click Here
</div>

Sign up to request clarification or add additional context in comments.

1 Comment

Great solution. I would recommand also to save the timer function in a class non-state variable, and in function component in a ref - so the component could be used many times with different timer functions.
13

I know this is an old question and i only shoot into the dark (did not test the code but i am sure enough it should work) but maybe this is of help to someone.

render() {
    let clicks = [];
    let timeout;

    function singleClick(event) {
        alert("single click");
    }

    function doubleClick(event) {
        alert("doubleClick");
    }

    function clickHandler(event) {
        event.preventDefault();
        clicks.push(new Date().getTime());
        window.clearTimeout(timeout);
        timeout = window.setTimeout(() => {
            if (clicks.length > 1 && clicks[clicks.length - 1] - clicks[clicks.length - 2] < 250) {
                doubleClick(event.target);
            } else {
                singleClick(event.target);
            }
        }, 250);
    }

    return (
        <a onClick={clickHandler}>
            click me
        </a>
    );
}

I am going to test this soon and in case update or delete this answer.

The downside is without a doubt, that we have a defined "double-click speed" of 250ms, which the user needs to accomplish, so it is not a pretty solution and may prevent some persons from being able to use the double click.

Of course the single click does only work with a delay of 250ms but its not possible to do it otherwise, you have to wait for the doubleClick somehow...

Comments

10

All of the answers here are overcomplicated, you just need to use e.detail:

<button onClick={e => {
  if (e.detail === 1) handleClick();
  if (e.detail === 2) handleDoubleClick();
}}>
  Click me
</button>

5 Comments

Yes, this is the best solution which simply uses web standards.
super, and easy, answer
This will run single click logic and double click handler. The Q seems to ask for a way to avoid running the single click handler if within a certain internal there is another click.
This is also a more Accessible option: "... accessibility options may extend this interval to make it easier to perform multiple clicks with adaptive interfaces." See: developer.mozilla.org/en-US/docs/Web/API/Element/…
This way listens to both events, the question was about to avoid this behavior, if it would be for listening to both you could just use onDoubleClick and it would be way more semantic
1

A simple example that I have been doing.

File: withSupportDoubleClick.js

let timer
let latestTouchTap = { time: 0, target: null }

export default function withSupportDoubleClick({ onDoubleClick = () => {}, onSingleClick = () => {} }, maxDelay = 300) {
  return (event) => {
    clearTimeout(timer)

    const touchTap = { time: new Date().getTime(), target: event.currentTarget }

    const isDoubleClick =
      touchTap.target === latestTouchTap.target && touchTap.time - latestTouchTap.time < maxDelay

    latestTouchTap = touchTap

    timer = setTimeout(() => {
      if (isDoubleClick) onDoubleClick(event)
      else onSingleClick(event)
    }, maxDelay)
  }
}

File: YourComponent.js

import React from 'react'
import withSupportDoubleClick from './withSupportDoubleClick'

export default const YourComponent = () => {

  const handleClick = withSupportDoubleClick({
    onDoubleClick: (e) => {
      console.log('double click', e)
    },
    onSingleClick: (e) => {
      console.log('single click', e)
    },
  })

  return (
    <div
      className="cursor-pointer"
      onClick={handleClick}
      onTouchStart={handleClick}
      tabIndex="0"
      role="button"
      aria-pressed="false"
    >
      Your content/button...
    </div>
  )
}
  • onTouchStart start is a touch event that fires when the user touches the element.

Comments

-4

Why do you describe these events handler inside a render function? Try this approach:

const Component = extends React.Component {

  constructor(props) {
      super(props);
  }

  handleSingleClick = () => {
    console.log('single click');
  }

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

  render() {
    return (
      <div onClick={this.handleSingleClick}  onDoubleClick={this.handleDoubleClick}>
      </div>
    );
  }
};

2 Comments

This will register a single & double click on a double click
Triggers both single click and double click at same time.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.