react-draggable
react-draggable copied to clipboard
OnClick inside Draggable doesn't work on mobile mode
Example: https://codesandbox.io/s/nervous-cdn-s2ufp
The desktop mode works ok. Mobile mode not fired the event (you can try on mobile or with dev tools - same result).
If it works for the first time - try to click several times, it's fired randomly
Also having this issue where clicking on child components on desktop correctly fire their event handler, but when I switch the view to any type of 'Responsive' in Chrome dev tools the event propagation seems to stop at the react-draggable component. Does this have something to do with MouseEvents vs. TouchEvents?
Not sure if this was the ideal solution but I noticed that tapping the child buttons on mobile mode was actually firing a touchstart
event instead of a click
event, so had to set up the buttons to have event handlers for both.
(Edited for clarity)
// component where dragging behavior is disabled under some circumstances.
// For mobile devices
// react-draggable disabled - fires both touchstart and click, so need to respond to only one of them
// react-draggable enabled - fires touchstart
// For desktop
// react-draggable disabled - fires click
// react-draggable enabled - fires click
...
const eventHandler = cb => {
return event => {
if (isMobile && event.type === 'touchstart') {
cb(event);
} else if (!isMobile && event.type === 'click') {
cb(event);
}
};
};
return (
<Button onClick={eventHandler(onClick)} onTouchStart={eventHandler(onClick)}>
<Icon type="arrow-left" />
</Button>
)
I have the same story, but I have an input, and click to move the cursor does not work on it. Unfortunately, the hacks will not work here, like changing the event. No ideas yet
Did you try to specify a cancel
selector and use it on the button?
I modified the example to show my problem.
cancel
- did not help
https://codesandbox.io/s/draggable-example-forked-vg1j3
Mobile browsers doesn't support proper mouse events
@myang5 's solution worked for me, thanks for the tip!
I simplified myang5's solution: onClick={(e) => handleSomething(e)} onTouchStart={(e) => handleSomething(e)}
I have the same case,Why is it designed like this ???
@myang5 's solution totally works in android chrome which I had exactly same issue but it would be awesome if we can get update with proper fixes. Thanks.
Also experiencing this issue.
Anybody knows what is causing this in react-draggable?
Ran into some stuff related to this today, probably caused by:
- To avoid the default behavior on mobile of scrolling on drag, in
DraggableCore
,touchstart
events getpreventDefault
called on them. - This prevents the
click
event that would normally be dispatched aftertouchstart
- The touch events in
DraggableCore
are registered manually viaref.addEventListener
because there's an issue callingpreventDefault
in touch events registered via React - Native event listeners fire before react event listeners, so even though the react draggable component is parent of the button, the draggable event listener fires first. Don't think it affects this situation but it gave me very unexpected behavior on mobile as I was trying to use
stopPropagation
insideonStart
andonDrag
- I wonder if it's possible to put
preventDefault
ontouchmove
rather thantouchstart
? I haven't had time to test this out
I meet the same pro,and what should i do now?
`// index.js
<Draggable
axis="both"
handle=".handle"
defaultPosition={{ x: 0, y: 0 }}
position={positionXY}
bounds="html"
offsetParent={document.body}
scale={1}
onStop={handleDragStop}
>
<div className="handle">
<Box></Box>
</div>
</Draggable>
//Box.js
<div onClick={handleClickBox}>
</div>`
Calculate the duration and displacement of the entire dragging process. If the duration is within 300ms and there is no displacement, the onclick event is triggered.
handleDragStart = () => {
this.dragStartAt = Date.now();
setTimeout(() => {
this.dragStartAt = 0;
}, 300);
}
handleDragStop = (e: TouchEvent, data: DraggableData) => {
const { onClickBtn } = this.props;
const change = {
x: this.state.position.x - data.x,
y: this.state.position.y - data.y,
};
// There is no displacement and the touch time is within the preset range, which is regarded as a click event.
if (change.x + change.y === 0 && this.dragStartAt) {
onClickBtn();
}
this.setPosition(data);
}
<Draggable
onStart={this.handleDragStart}
onStop={this.handleDragStop}
/>
This may not be the most ideal solution, it would be really great if this problem could be solved soon.
Another version of @00Jane's, but for functional components:
const DragBox = ({ children }) => {
let [dragInfo, setDragInfo] = useState(null)
let handleDragStart = (e, data) => {
setDragInfo({
x: data.x,
y: data.y,
time: Date.now()
})
}
let handleDragStop = (e, data) => {
if (!dragInfo) return
let change = {
x: Math.abs(data.x - dragInfo.x),
y: Math.abs(data.y - dragInfo.y),
time: Date.now() - dragInfo.time
}
if (change.x + change.y <= 10 && change.time < 300) e.srcElement?.click?.()
}
return (
<Draggable onStart={handleDragStart} onStop={handleDragStop}>
{children}
</Draggable>
)
}