admin管理员组文章数量:1131143
I am building a Minesweeper game with React and want to perform a different action when a cell is single or double clicked. Currently, the onDoubleClick
function will never fire, the alert from onClick
is shown. If I remove the onClick
handler, onDoubleClick
works. Why don't both events work? Is it possible to have both events on an element?
/** @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'));
I am building a Minesweeper game with React and want to perform a different action when a cell is single or double clicked. Currently, the onDoubleClick
function will never fire, the alert from onClick
is shown. If I remove the onClick
handler, onDoubleClick
works. Why don't both events work? Is it possible to have both events on an element?
/** @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'));
Share
Improve this question
edited Dec 15, 2015 at 17:57
davidism
127k30 gold badges415 silver badges347 bronze badges
asked Sep 11, 2014 at 1:57
thisisnotabusthisisnotabus
2,0693 gold badges16 silver badges31 bronze badges
4
|
15 Answers
Reset to default 78This is not a limitation of React, it is a limitation of the DOM's click
and dblclick
events. As suggested by Quirksmode's click documentation:
Don't register click and dblclick events on the same element: it's impossible to distinguish single-click events from click events that lead to a dblclick event.
For more current documentation, the W3C spec on the dblclick
event states:
A user agent must dispatch this event when the primary button of a pointing device is clicked twice over an element.
A double click event necessarily happens after two click events.
Edit:
One more suggested read is jQuery's dblclick
handler:
It is inadvisable to bind handlers to both the click and dblclick events for the same element. The sequence of events triggered varies from browser to browser, with some receiving two click events before the dblclick and others only one. Double-click sensitivity (maximum time between clicks that is detected as a double click) can vary by operating system and browser, and is often user-configurable.
Instead of using ondoubleclick
, you can use event.detail
to get the current click count. It's the number of time the mouse's been clicked in the same area in a short time.
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>;
In the example above, if you triple click the button it will print all 3 cases:
click
double click
triple click
Live Demo
The required result can be achieved by providing a very slight delay on firing off the normal click action, which will be cancelled when the double click event will happen.
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>
You can use a custom hook to handle simple click and double click like this :
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);
}
then in your component you can use :
const click = useSingleAndDoubleClick(callbackClick, callbackDoubleClick);
<button onClick={click}>clic</button>
Edit:
I've found that this is not an issue with React 0.15.3.
Original:
For React 0.13.3, here are two solutions.
1. ref callback
Note, even in the case of double-click, the single-click handler will be called twice (once for each click).
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
I had another solution that used lodash
, but I abandoned it because of the complexity. The benefit of this was that "click" was only called once, and not at all in the case of "double-click".
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;
on the soapbox
I appreciate the idea that double-click isn't something easily detected, but for better or worse it IS a paradigm that exists and one that users understand because of its prevalence in operating systems. Furthermore, it's a paradigm that modern browsers still support. Until such time that it is removed from the DOM specifications, my opinion is that React should support a functioning onDoubleClick
prop alongside onClick
. It's unfortunate that it seems they do not.
Here's what I have done. Any suggestions for improvement are welcome.
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>
</>
)
}
}
Typescript React hook to capture both single and double clicks, inspired by @erminea-nea 's answer:
import {useEffect, useState} from "react";
export function useSingleAndDoubleClick(
handleSingleClick: () => void,
handleDoubleClick: () => void,
delay = 250
) {
const [click, setClick] = useState(0);
useEffect(() => {
const timer = setTimeout(() => {
if (click === 1) {
handleSingleClick();
}
setClick(0);
}, delay);
if (click === 2) {
handleDoubleClick();
}
return () => clearTimeout(timer);
}, [click, handleSingleClick, handleDoubleClick, delay]);
return () => setClick(prev => prev + 1);
}
Usage:
<span onClick={useSingleAndDoubleClick(
() => console.log('single click'),
() => console.log('double click')
)}>click</span>
2024 Update
Thank you '@Erminea Nea' for your great answer.
I just wanted to share my improvement to that hook, adding a return ref of the executed event:
import { useState, useEffect, MouseEvent } from 'react';
const useSingleAndDoubleClick = (
actionSimpleClick: (e: MouseEvent) => void,
actionDoubleClick: (e: MouseEvent) => void,
delay = 250
) => {
const [state, setState] = useState<{ click: number; e: MouseEvent | null }>({ click: 0, e: null });
useEffect(() => {
const timer = setTimeout(() => {
// simple click
if (state.click === 1) actionSimpleClick(state.e!);
setState({ e: state.e, click: 0 });
}, delay);
// the duration between this click and the previous one
// is less than the value of delay = double-click
if (state.click === 2) actionDoubleClick(state.e!);
return () => clearTimeout(timer);
}, [state, actionSimpleClick, actionDoubleClick, delay]);
return (e: MouseEvent) => {
setState({ click: state.click + 1, e });
};
};
export default useSingleAndDoubleClick;
And the usage:
const onSingleClick = (e?: MouseEvent): void => {
console.log('single click');
e && e.stopPropagation();
};
const onDoubleClick = (e?: MouseEvent): void => {
console.log('double click');
e && e.stopPropagation();
};
const onVideoClick = useSingleAndDoubleClick(onSingleClick, onDoubleClick);
return (<div onClick={onVideoClick}> Click me</div>);
Here's my solution for React in TypeScript:
import { debounce } from 'lodash';
const useManyClickHandlers = (...handlers: Array<(e: React.UIEvent<HTMLElement>) => void>) => {
const callEventHandler = (e: React.UIEvent<HTMLElement>) => {
if (e.detail <= 0) return;
const handler = handlers[e.detail - 1];
if (handler) {
handler(e);
}
};
const debounceHandler = debounce(function(e: React.UIEvent<HTMLElement>) {
callEventHandler(e);
}, 250);
return (e: React.UIEvent<HTMLElement>) => {
e.persist();
debounceHandler(e);
};
};
And an example use of this util:
const singleClickHandler = (e: React.UIEvent<HTMLElement>) => {
console.log('single click');
};
const doubleClickHandler = (e: React.UIEvent<HTMLElement>) => {
console.log('double click');
};
const clickHandler = useManyClickHandlers(singleClickHandler, doubleClickHandler);
// ...
<div onClick={clickHandler}>Click me!</div>
Based on the solution by Erminea Nea and the improvements by Stanislau Buzunko (great work!
本文标签: javascriptonClick works but onDoubleClick is ignored on React componentStack Overflow
版权声明:本文标题:javascript - onClick works but onDoubleClick is ignored on React component - Stack Overflow 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1736766530a1951851.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
dblclick
event. See my answer for why it's not possible to prevent the first click. – Ross Allen Commented Sep 11, 2014 at 6:43