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

OnClick inside Draggable doesn't work on mobile mode

Open LennyLip opened this issue 3 years ago • 18 comments

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 avatar Apr 03 '21 09:04 LennyLip

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

LennyLip avatar Apr 03 '21 12:04 LennyLip

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 avatar Apr 07 '21 18:04 myang5

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

myang5 avatar Apr 07 '21 19:04 myang5

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

shenng1 avatar Apr 09 '21 16:04 shenng1

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

moklick avatar Apr 09 '21 17:04 moklick

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

shenng1 avatar Apr 09 '21 17:04 shenng1

Mobile browsers doesn't support proper mouse events

sankarnarayanansr avatar Apr 20 '21 11:04 sankarnarayanansr

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

hesto2 avatar May 25 '21 00:05 hesto2

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

FoxyWolfy avatar Jul 29 '21 03:07 FoxyWolfy

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

WuQiuYang avatar Aug 24 '21 08:08 WuQiuYang

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

delia-m avatar Sep 02 '21 03:09 delia-m

Also experiencing this issue.

Anybody knows what is causing this in react-draggable?

pandaiolo avatar Sep 23 '21 21:09 pandaiolo

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 get preventDefault called on them.
  • This prevents the click event that would normally be dispatched after touchstart
  • The touch events in DraggableCore are registered manually via ref.addEventListener because there's an issue calling preventDefault 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 inside onStart and onDrag
  • I wonder if it's possible to put preventDefault on touchmove rather than touchstart? I haven't had time to test this out

nmkataoka avatar Oct 19 '21 22:10 nmkataoka

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

sujiangyin avatar Dec 07 '21 08:12 sujiangyin

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.

00Jane avatar Aug 21 '22 14:08 00Jane

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

hufftheweevil avatar Dec 05 '22 19:12 hufftheweevil