react-draggable
react-draggable copied to clipboard
Prevent redirect on dragging div with anchor tag
How can I prevent the page redirect while dragging an anchor tag but still redirect when I only clicked it?
Tried using preventDefault() inside onDrag handler but it also disable the anchor tag when only clicking
Also looking for a solution to this, I have a <Draggable> that contains a child with a click event, I want it to only fire when not dragging the parent. I attempted to use states to determine it, or preventing the event from defaulting, but nothing worked
Is your anchor tag have to be draggable? If not, you can use the cancel option. Your link will be clickable but not draggable.
I have the same problem, I have clickeable divs, but the event.stopPropagation() method is not working at all, the event is propagated after the onStop. My onStop handler looks like this:
onEndDrag = event => {
event.stopPropagation();
event.preventDefault();
...
}
A very ugly solution I implemented is to lock all clicks while the user is dragging, somethig like this:
//component mount
this._ref.removeEventListener("click", this.checkClickPropagation);
//onDrag
onDrag = event => {
this.dragging = true;
};
onEndDrag = event => {
if(this.dragging) {
setTimeout(() => {
this.dragging = false;
}, 500)
} else {
this.dragging = false;
}
}
checkClickPropagation = e => {
if(this.dragging === true) {
e.preventDefault();
e.stopPropagation();
}
}
//and your jsx
<Draggable>
<div onClick={this.checkClickPropagation} ref={c=>this._ref=c}>
</div>
</Draggable>
I solved this issue as follows:
- Set a 'dragging' flag in the drag start event handler
- Clear that flag (using a timer) 250ms after the drag end event
- Have the click handler only execute its contents if the flag is cleared (not in a drag operation)
Is there no better way to do this than setting a "dragging" flag?
Wrapping the <Draggable> with a <div> with an event.stopPropagation() worked for me.
<div onClick={e => e.stopPropagation()}>
<Draggable
axis="x"
defaultClassName="DragHandle"
defaultClassNameDragging="DragHandleActive"
onDrag={(event, { deltaX }) =>
this.resizeRow({
dataKey,
columnIndex,
deltaX,
totalTableWidth,
})
}
position={{ x: 0 }}
>
<span className="DragHandleIcon">⋮</span>
</Draggable>
</div>
Here is a solution with React hooks similar to timeout solution
const [isDragging, setDragging] = React.useState(false)
const currentTimeOut = React.useRef(null)
const onDragStop = React.useCallback(() => { // can be done onDragStart also
setDragging(true)
currentTimeOut.current = setTimeout(() => {
setDragging(false)
// you can also reduce the time amount to 250ms or what ever you prefer
}, 600)
// do your operations here
}, [])
// !important -> clearInterval to avoid errors on unmount
React.useEffect(() => clearInterval(currentTimeOut.current), [])
`
I approached it via dynamic css classnames. When you are dragging the div, you essentially remove the touch via pointer-events: none
import React, { FC, ReactNode, useState } from "react";
import Draggable from "react-draggable";
const YourComponent: FC = () => {
const [isDragged, setIsDragged] = useState(false);
const wrapper = isDragged ? "your-component--dragged" : "your-component";
const onDragStart = () => {
setIsDragged(true);
};
const onDragEnd = () => {
setIsDragged(false);
};
return (
<Draggable onDrag={onDragStart} onStop={onDragEnd}>
<div className={wrapper} />
</Draggable>
);
};
export default YourComponent;
And in the css for the component:
.your-component,
.your-component--dragged {
height: 200px;
width: 200px;
background-color: red;
}
.your-component--dragged {
pointer-events: none;
}