Unfortunately, this didn’t work. You did, however, help me clean up some code where I was setting the selection after the editor was mounted. This is much cleaner, so thank you!
My scenario: I mount the editor after a user clicks on a static element on the page, it isn’t in the DOM when the page loads initially.
Ah I re-read this more carefully.
You need to figure a way to focus the editor after it has been mounted onto the dom. This is because you can’t focus on an element that’s not currently in the dom.
Thanks for that info. I create an EditorView in a useEffect hook, then call view.focus() on it. The view is appended in the constructor. Just to be sure, I tried appending it myself like you do, but it didn’t make a difference. This only happens on ios, its weird.
If you access that in an iPhone (using Safari) or an Android device (using Chrome), you will see that the editor receives focus, but the keyboard doesn’t become shown.
I also tried adding autofocus to the dom element before appending it. That caused the same outcome, although I no longer had to call focus() for the editor to have focus.
I have that handle in my component that has the ProseMirror view, and then I call that focus function with a timeout of 0 to make sure the focus works fine on mobile.
All of the things you saw in my test app were direct tests of what bhl recommended. Those didn’t work any better than my production implementation does, unfortunately.
I do have a question though. I don’t see anywhere in your useProsemirror hook that you are focusing on mount like I am trying to do. Am I missing it? Do you have an app which does this that I could test on my iPhone?
Thanks for the help. I feel like I must not have been the only person to face this issue…so there must be a solution.
Oh hey, I’m sorry I totally blanked on this: this is a well-known limitation or user-protection thing (depending on how you look at it) in iOS Safari: it is impossible to focus an element on a stack that did not originate from user interaction!
The reason I blanked on this is because I’ve been developing on iOS using Capacitor, which does not have this limitation because instead of iOS Safari it uses a WKWebView, which lets you enable non-user-interaction-originated focus.
So yeah, this behavior is totally “broken” on iOS Safari.
Tip #3: Though programmatically setting focus on an input box won’t cause the keyboard to open, you can set focus on another input while handling the touchDown event; the soft keyboard still opens. Pairing this with #2 means that you can position a fake text input box near the bottom of the page, and when that’s tapped instead set focus to the real input box which is higher on the page (even if it’s offscreen, e.g. placed at top=0 left=99999px) … then, use setTimeout to run a function in a few milliseconds which moves that real input box down lower on the page to where you really wanted it.
But I’ve never tried anything like this. To be honest I’m not even sure I understand it .
Well, I figured it probably wouldn’t let me trick iOS like that, and I was right. Bummer! I guess we’ll have to figure out some UI for dealing with this. Thank you again.
Just to close out this thread, I finally figured it out. I needed to use pointer events rather than onClick. I ended up using onPointerDown and pepjs to polyfill.
I have a few different editors in my app. One of them works great just by rendering it and then calling view.focus(). Two of them require me to do this (I have no idea why non-iOS is different in this situation, but it works):
@brandoncc Thank you. So you click on a container that looks like the editor,and if for some reason you handle the click with onPointerDown—and the other details you write above—after you’ve inserted the editor, view.focus() will focus the editor and open the keyboard?
Did you try touch events, by the way? Last time I checked (I believe it was iOS 13), iOS didn’t support PointerEvents but did support TouchEvents. Guessing maybe that polyfill translates pointer events to touch events.
Okay, I think I know why this magically worked, even though everything said it shouldn’t. OnPointerDown is causing my overlay element to be removed and the editor to be rendered in its place. By the time the user lifts their finger, the element under their finger is the editor, so they are triggering a touchend on that element.
Thank you very much for your detailed explanation. I encountered the exact same issue. After changing onClick to onPointerDown, the problem was resolved. Thank you!