Skip to content

Conversation

maiconcarraro
Copy link

@maiconcarraro maiconcarraro commented Sep 14, 2025

fixes: #130

only happens in iPhone, when trying to move caret in an input it's blocking the movement because it triggers touchmove, the same is not triggered for Android devices or Chrome dev tools.

tried to add a robust solution to only check for elements in the shards, but it could be even simpler if move the logic to the shouldPrevent and check if !!window.getSelection().anchorNode.

let me know if you need help to simulate or any issues.

@maiconcarraro
Copy link
Author

maiconcarraro commented Sep 14, 2025

for people looking for workaround while this is not merged, I'm using this approach:

// useAllowTouchSelection.tsx

import * as React from "react";

export default function useAllowTouchSelection() {
  React.useEffect(() => {
    const nonPassive = { passive: false };

    const checkTouchingSelection = (event: TouchEvent) => {
      // selection activated, stop react-remove-scroll listeners
      if (document.getSelection()?.anchorNode) {
        event.stopImmediatePropagation(); // only the immediate works
        return;
      }
      // let others listeners execute as usual
    };

    if (!isIphone()) {
      // optional this part, tried to isolate only for iPhone devices
      return;
    }

    document.addEventListener("touchmove", checkTouchingSelection, nonPassive);

    return () => {
      document.removeEventListener(
        "touchmove",
        checkTouchingSelection,
        nonPassive as any,
      );
    };
  }, []);

and you can import to your root element, or in the same Dialog component:

function DialogContent({
  className,
  children,
  ...props
}: React.ComponentPropsWithRef<typeof DialogPrimitive.Content>) {
  useAllowTouchSelection(); // here

  return (
    <DialogPortal data-slot="dialog-portal">
      <DialogOverlay />
      <DialogPrimitive.Content
        data-slot="dialog-content"
        className={cn("...", className)}
        {...props}
      >
        {children}
        <DialogPrimitive.Close className="...">
          <IconClose />
          <span className="sr-only">Close</span>
        </DialogPrimitive.Close>
      </DialogPrimitive.Content>
    </DialogPortal>
  );
}

the crucial step is to make sure the hook (or component) runs first to attach listeners first and stop propagation correctly.

@antheus-s
Copy link

I think that checkTouchingSelection is missing in the useEffect dependencies where we set the event listeners. Other than that, this seems to be working perfectly.

@maiconcarraro
Copy link
Author

I think that checkTouchingSelection is missing in the useEffect dependencies where we set the event listeners. Other than that, this seems to be working perfectly.

@antheus-s right, tbh not even need the useCallback, since there is no actual deps, you can just move to be a function inside of the useEffect so it can properly attach/remove, and happy to see it worked for you (same for me) 🙌

@lovlka
Copy link

lovlka commented Sep 17, 2025

Thanks @maiconcarraro! Can confirm that the workaround works great for my case too.

Fingers crossed this can be merged and incorporated into Radix UI as well!

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

Successfully merging this pull request may close these issues.

iOS text selection handles break when react-remove-scroll is used in radix-ui's Dialog component

3 participants