Skip to content
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

JS.focus does not work on iOS for text fields #3563

Closed
MitchellJeppson opened this issue Dec 8, 2024 · 1 comment · Fixed by #3657
Closed

JS.focus does not work on iOS for text fields #3563

MitchellJeppson opened this issue Dec 8, 2024 · 1 comment · Fixed by #3657

Comments

@MitchellJeppson
Copy link

Environment

  • Elixir version (elixir -v): Elixir 1.16.0 (compiled with Erlang/OTP 26)
  • Phoenix version (mix deps): 1.7.14
  • Phoenix LiveView version (mix deps): 1.0.0
  • Operating system: Mac OS Sequoia 15.1.1
  • Browsers you attempted to reproduce this bug on (the more the merrier): iOS Safari, iOS Chrome, Android Chrome, Mac OS Safari and Chrome
  • Does the problem persist after removing "assets/node_modules" and trying again? Yes/no: yes

Actual behavior

Using JS.focus does not focus a text field if you are on iOS mobile. Here is some discussion and a simplified example of the issue. Sound like safari doesn't like to pop up the virtual keyboard unless it is a directly in response to user action. In that linked discussion above it was floated that it could be due to the debounce logic.

@SteffenDE
Copy link
Collaborator

So the issue is caused by the JS focus code running inside a requestAnimationFrame:

exec_focus(e, eventType, phxEvent, view, sourceEl, el){
window.requestAnimationFrame(() => ARIA.attemptFocus(el))
},
exec_focus_first(e, eventType, phxEvent, view, sourceEl, el){
window.requestAnimationFrame(() => ARIA.focusFirstInteractive(el) || ARIA.focusFirst(el))
},

But we cannot just remove that call, because imagine the (previously) default modal code:

  defp show_modal(js \\ %JS{}, id) when is_binary(id) do
    js
    |> JS.show(to: "##{id}")
    |> JS.show(
      to: "##{id}-bg",
      transition: {"transition-all transform ease-out duration-300", "opacity-0", "opacity-100"}
    )
    |> show("##{id}-container")
    |> JS.add_class("overflow-hidden", to: "body")
    |> JS.focus_first(to: "##{id}-content")
  end

which does multiple JS commands at once, trying to focus the first element inside the modal at the end. This only works when the focus waits for the element to be visible and because the JS.show also uses requestAnimationFrame, we need to wait.

We could maybe do this, which would try to focus twice:

   exec_focus(e, eventType, phxEvent, view, sourceEl, el){
+    ARIA.attemptFocus(el)
     window.requestAnimationFrame(() => ARIA.attemptFocus(el))
   },

   exec_focus_first(e, eventType, phxEvent, view, sourceEl, el){
+    ARIA.focusFirstInteractive(el) || ARIA.focusFirst(el)
     window.requestAnimationFrame(() => ARIA.focusFirstInteractive(el) || ARIA.focusFirst(el))
   },

but I'm not 100% sure that this is always fine.

I'll try to look into if we can remove the requestAnimationFrame for show, but it was added for a specific reason: 9b67518

SteffenDE added a commit that referenced this issue Jan 31, 2025
This ensures that JS.focus works on iOS.

Fixes: #3563
SteffenDE added a commit that referenced this issue Feb 1, 2025
This ensures that JS.focus works on iOS.

Fixes: #3563
SteffenDE added a commit that referenced this issue Feb 1, 2025
This ensures that JS.focus works on iOS.

Fixes: #3563
SteffenDE added a commit that referenced this issue Feb 23, 2025
This reverts commit 485c349.

We need to revert because this changes the behavior of JS.show |> JS.show
where the second JS show relied on the first one not being applied yet.

We will keep the change in 1.1 and document it.

This means that #3563 will
be broken again on 1.0.5 :(
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 a pull request may close this issue.

2 participants