-
Notifications
You must be signed in to change notification settings - Fork 124
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
Bullet/Editable: Use fastClick for clickHandler #2752
base: main
Are you sure you want to change the base?
Bullet/Editable: Use fastClick for clickHandler #2752
Conversation
@@ -203,6 +203,8 @@ const Editable = ({ | |||
// set offset to null to allow the browser to set the position of the selection | |||
let offset = null | |||
|
|||
if (isTouch && isSafari() && contentRef.current?.innerHTML.length) offset = contentRef.current.innerHTML.length |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The caret was getting positioned at the end of the thought somehow onFocus
, so I wanted to preserve that behavior onTap
.
@@ -51,6 +51,7 @@ const useEditMode = ({ | |||
selection.clear() | |||
} else { | |||
selection.set(contentRef.current, { offset: editingCursorOffset || 0 }) | |||
if (isTouch && isSafari()) contentRef.current?.focus() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not quite sure why this is necessary, but it was having the following problem on iOS Safari:
- Tap a thought - cursor moves, keyboard stays closed
- Tap it again - keyboard opens
- Close keyboard
- Tap it again - keyboard does not open
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, good catch.
I'll create a separate issue to get to the bottom of this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you, @ethan-james!
Switching to fastClick
seems to have done the trick, when I test locally:
thoughts.mp4
Co-authored-by: Trevin Hofmann <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the submission! I can confirm that the timing is correct now.
The only problem I'm seeing is that the selection offset is not correct when tapping in the middle of a non-cursor thought in edit mode. This behavior relies on the default browser behavior to set the selection underneath the user's finger.
Steps to Reproduce
- Hello World
- x
- Set the cursor on
x
. - Tap
x
to open the keyboard if it's not open already. - Tap in between
Hello
andWorld
.
Current Behavior
The caret is placed at the end of the thought, i.e. Hello World|
Expected Behavior
main
The caret should be placed in between Hello
and World
, i.e. Hello| World
.
When I tested it on the |
Do you know why the My sense is that it would be more reliable to fix the root cause of the keyboard not appearing rather than trying to restore the selection and offset after the fact. But let me know what you find out! |
It used to call All of the advice on Google for eliminating click/focus delays is 10 years old and doesn't work, but I did just now put together a CodePen (https://codepen.io/ethan-james-the-sasster/pen/ByBYqrL) showing that the delay doesn't occur on simple Last thing to point out, though, is that on the iPad I'm using, the native focus behavior places the caret at the end of the editable text, and there doesn't seem to be a way to tap it into a different location. I've already emulated that behavior, although I agree that letting the browser control it natively is better. |
Also, I wasn't able to find a way to get the offset from a
|
Yes, exactly. The delay is not inherent to contenteditables, but is being created through our own event handling. There is admittedly a lot of complexity with the selection handling, including automatically setting the selection when a thought re-renders, via
Interesting. My last few iPhones have correctly set the caret in the middle of the thought in these cases. I haven't tested on iPad recently, but I would assume with the same version of iOS it would behave the same. What version of iOS are you running? Do you possibly have another device or way to reproduce the default caret behavior? It might be difficult to troubleshoot this otherwise.
Yes, that's what I was worried about. It's plausible, but strikes me as high effort + high risk. Plus, inserting a DOM element and measuring it may introduce a delay itself, negating the benefit of the fastClick. |
Here's what I see on an iPhone running 18.1.1. You can see that I can tap to the end of the editable div, or to the beginning, or select all. ScreenRecording_01-09-2025.09-39-04_1.MP4 |
I just tried it with longer text in the editable div, and I see that you can also tap to the end of any given word. 🤦 |
Yes, that too! In that case, I really don't recommend trying to programmatically recreate the default caret offset behavior on tap. |
Yes, I would like to avoid it as well! I'm looking into why the |
Thanks. Let me know if any of the existing selection logic needs clarification. I may not be able to explain all the details that have built up over time, but I'll do my best to elucidate anything that is particularly confusing. |
Good catch! Interesting that that changed the |
Long press timing should be unaffected, and I tested drag-and-drop some more. Without any If I set There was always a 100ms delay (even with So as things stand, I think that setting |
Fix #1691
The
Bullet
component was usingonClick
to dispatch itssetCursor
action, which has a noticeable delay on iOS. Wrapping the click handler infastClick
solves the issue.In
Editable
, theonTap
handler was ignoring taps that would cause the keyboard to open, and falling back to theonFocus
handler after a 300ms delay. It was necessary to intercept those taps and set the cursor with a manual offset to put the caret back at the end of the thought on iOS Safari. There was also some flakiness whereselection.set
wasn't always opening the keyboard, so now it callscontentRef.current.focus()
explicitly.