react-draggable icon indicating copy to clipboard operation
react-draggable copied to clipboard

How to prevent execution of "click" event after drag?

Open akaribrahim opened this issue 3 years ago • 9 comments

Hi, I have Draggable element and inside of it, I have a component with onClick event. At the end of the drag, the click event is triggered. My question is how to prevent this action and how to seperate these to event? Thanks. Ekran Alıntısı

akaribrahim avatar Nov 22 '20 19:11 akaribrahim

There's not any way that I'm aware of to stop the event from reaching the child. I assume you want a click to keep working, but a drag to prevent the click from happening. You can accomplish this with some local state.

https://codesandbox.io/s/nervous-burnell-e2r95?file=/src/App.js

STRML avatar Nov 22 '20 20:11 STRML

Same problem

Holybasil avatar Dec 15 '20 11:12 Holybasil

Hi guys, you may take a look at this example https://codesandbox.io/s/material-demo-gc0le?file=/demo.js from https://stackoverflow.com/questions/59136239/how-handle-long-press-event-in-react-web. You may consider drag as a 'long press'. So you can distinguish between a click event and a drag event by checking their pressing time. Just need to return the onClick event if a long press is detected. onClick={(e) => { e.stopPropagation() if (isCommandHandled) return //some actions here }}

marcocheungkami avatar Dec 16 '20 09:12 marcocheungkami

Hi guys,

I just had a simular problem, and the way I solved it was by checking if a drag happened by checking the deltaX and Y. If not drag happened, then I call the "onClick" handler. Maybe this could be incorporated in the source code? I would gladly send a PR

imanderson avatar Apr 23 '21 14:04 imanderson

I use this, to handle the drag only after a time lapse

const PRESS_TIME_UNTIL_DRAG_MS = 250:

const IconDraggable = ({ children, onClick }) => {
  const [isDragging, setDragging] = useState(false)

  const handleClick = () => {
    if (isDragging === true) {
      return
    }

    onClick()
  }

  return (
    <Draggable
      onStart={() => setTimeout(() => setDragging(true) , PRESS_TIME_UNTIL_DRAG_MS)}
      onDrag={e => {
          if (isDragging === false) {
             e.preventDefault()
            return false
          }
      }}
      >
      <div style={{ position: 'absolute' }} onClick={() => handleClick(isDragging)}>
        {children}
      </div>
    </Draggable>
  )
}

pbenard73 avatar Jan 19 '22 18:01 pbenard73

Hi guys,

I just had a simular problem, and the way I solved it was by checking if a drag happened by checking the deltaX and Y. If not drag happened, then I call the "onClick" handler. Maybe this could be incorporated in the source code? I would gladly send a PR

I've tried all the solutions above and this seems like the best solution, where less accurate clicks that drag for a few pixels are still counted as clicks. The below code shows the logic implemented.

`

const [dragStartPos, setDragStartPos] = useState({ x: 0, y: 0 });

const onStart = (e) => {
    setDragStartPos({ x: e.screenX, y: e.screenY });
};

const onStop = (e) => {
    const dragX = Math.abs(dragStartPos.x - e.screenX);
    const dragY = Math.abs(dragStartPos.y - e.screenY);
    if (dragX < 5 || dragY < 5) {
        console.log(`click with drag of ${dragX}, ${dragY}`);
        // onClick functionality here
    } else {
        console.log(`click cancelled with drag of ${dragX}, ${dragY}`);
    }
};

`

MichaelKim39 avatar Jul 28 '22 17:07 MichaelKim39

Using a ref to store the "isDragged" state works well for me.

const Parent: React.FC<Props> = () => {

  const draggedRef = useRef<boolean>(false)

  return (
    <Draggable
      handle=".handle"
      onDrag={() => {
        draggedRef.current = true
      }}
    >
      <div className="handle">
        <Child draggedRef={draggedRef}/>
      </div>
    </Draggable>
  )
}

const Child: React.FC<Props> = ({
  draggedRef,
}: {
  draggedRef: React.MutableRefObject<boolean>
}) => {
  function onClick() {
    const dragged = draggedRef.current
    draggedRef.current = false
    if (!dragged) {
      alert('button was clicked but not dragged')
    }
  }

  return <button onClick={onClick}>A Button</button>
}

KDKHD avatar Oct 31 '23 00:10 KDKHD

add this css to your child wrapper comp.

for example

 <Draggable ...>
     <ChildCompWrapper isDragging={isDragging}>
              {...............}
     </ChildCompWrapper>    
 </Draggable>  

const ChildCompWrapper = styled.divpointer-events: ${({ isDragging }) => isDragging && 'none'};

developerjhp avatar Nov 14 '23 01:11 developerjhp

Using a ref to store the "isDragged" state works well for me.

const Parent: React.FC<Props> = () => {

  const draggedRef = useRef<boolean>(false)

  return (
    <Draggable
      handle=".handle"
      onDrag={() => {
        draggedRef.current = true
      }}
    >
      <div className="handle">
        <Child draggedRef={draggedRef}/>
      </div>
    </Draggable>
  )
}

const Child: React.FC<Props> = ({
  draggedRef,
}: {
  draggedRef: React.MutableRefObject<boolean>
}) => {
  function onClick() {
    const dragged = draggedRef.current
    draggedRef.current = false
    if (!dragged) {
      alert('button was clicked but not dragged')
    }
  }

  return <button onClick={onClick}>A Button</button>
}

Thanks It Worked!!!

ChoiYongWon avatar Mar 27 '24 10:03 ChoiYongWon