Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
akaribrahim opened this issue Nov 22, 2020 · 9 comments
Open

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

akaribrahim opened this issue Nov 22, 2020 · 9 comments

Comments

@akaribrahim
Copy link

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ı

@STRML
Copy link
Collaborator

STRML commented Nov 22, 2020

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

@Holybasil
Copy link

Same problem

@marcocheungkami
Copy link

marcocheungkami commented Dec 16, 2020

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 }}

@imanderson
Copy link

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

@pbenard73
Copy link

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>
  )
}

@MichaelKim39
Copy link

MichaelKim39 commented Jul 28, 2022

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}`);
    }
};

`

@KDKHD
Copy link

KDKHD commented Oct 31, 2023

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>
}

@developerjhp
Copy link

add this css to your child wrapper comp.

for example

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

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

@ChoiYongWon
Copy link

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!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants