react-grid-layout / react-draggable

React draggable component
MIT License
9k stars 1.03k forks source link

OnClick inside Draggable doesn't work on mobile mode #550

Open LennyLip opened 3 years ago

LennyLip commented 3 years ago

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

LennyLip commented 3 years ago

If it works for the first time - try to click several times, it's fired randomly

myang5 commented 3 years ago

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?

myang5 commented 3 years ago

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>
)
shenng1 commented 3 years ago

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

moklick commented 3 years ago

Did you try to specify a cancel selector and use it on the button?

shenng1 commented 3 years ago

I modified the example to show my problem. cancel - did not help https://codesandbox.io/s/draggable-example-forked-vg1j3

sankarnarayanansr commented 3 years ago

Mobile browsers doesn't support proper mouse events

hesto2 commented 3 years ago

@myang5 's solution worked for me, thanks for the tip!

FoxyWolfy commented 3 years ago

I simplified myang5's solution: onClick={(e) => handleSomething(e)} onTouchStart={(e) => handleSomething(e)}

WuQiuYang commented 3 years ago

I have the same case,Why is it designed like this ???

delia-m commented 3 years ago

@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.

pandaiolo commented 3 years ago

Also experiencing this issue.

Anybody knows what is causing this in react-draggable?

nmkataoka commented 2 years ago

Ran into some stuff related to this today, probably caused by:

sujiangyin commented 2 years ago
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>`
00Jane commented 2 years ago

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.

hufftheweevil commented 1 year ago

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>
  )
}
aeokay commented 4 days ago

3 years for this bug, still now. 👎