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

bug: unintended re-focus to input after selecting clear input button with iOS VoiceOver #29358

Closed
3 tasks done
nagashimam opened this issue Apr 18, 2024 · 8 comments · Fixed by #29366 · May be fixed by l00pinfinity/devvscape-code-humor#40 or YoutacRandS-VA/eth2-beaconchain-explorer-app#2
Labels
package: core @ionic/core package type: bug a confirmed bug report

Comments

@nagashimam
Copy link

nagashimam commented Apr 18, 2024

Prerequisites

Ionic Framework Version

v7.x, v8.x (Beta)

Current Behavior

When using iOS VoiceOver to focus on an ion-input with a clear input button, and swiping right to select the next item, the focus unexpectedly returns to the input immediately after the clear input button gains focus. Consequently, it becomes impossible to move VoiceOver's focus away from the input, effectively preventing interaction with other parts of the app.

I have attached a screen recording to demonstrate this behavior.

Expected Behavior

The VoiceOver focus should remain on the clear input button and not revert back to the input field after it has been selected.

Steps to Reproduce

  • Open the reproduction page on an iPhone.
  • Use the Safari Developer Tools and execute the following commands to monitor focusin events:
document.querySelector("body > ion-app > ion-content > ion-list > ion-item:nth-child(1) > ion-input > label > div.native-wrapper.sc-ion-input-ios.sc-ion-input-ios-s > button").addEventListener("focusin", () => {console.log("focusin to button")});
document.querySelector("body > ion-app > ion-content > ion-list > ion-item:nth-child(1) > ion-input").addEventListener("focusin", () => {console.log("focusin to ion-input")});
  • Enable VoiceOver, enter some text in the first input field, and swipe right.
    • Observe that the focus unexpectedly returns to the input immediately after the clear input button gains focus.
    • Notice that the ion-input's focusin event is triggered after the button’s event.
  • Remove the event listener for the ion-input (as demonstrated in the uploaded screen recording) and repeat the previous step.
    • The problematic behavior no longer occurs.

Code Reproduction URL

https://stackblitz.com/edit/usdwwp?file=index.html

Ionic Info

I use online StackBlitz to reproduce the issue using @ionic/core@8.0.0, but just in case information about my local environment is needed:

Ionic:
                                                                                                                                                                                                                  
   Ionic CLI                     : 7.2.0 (/Users/mzb0005/.anyenv/envs/nodenv/versions/20.11.1/lib/node_modules/@ionic/cli)
   Ionic Framework               : @ionic/angular 7.8.0
   @angular-devkit/build-angular : 17.3.0
   @angular-devkit/schematics    : 17.3.0
   @angular/cli                  : 17.3.0
   @ionic/angular-toolkit        : 11.0.1
                                                                                                                                                                                                                  
Cordova:
                                                                                                                                                                                                                  
   Cordova CLI       : 12.0.0 (cordova-lib@12.0.1)
   Cordova Platforms : android 12.0.1, ios 7.0.1
   Cordova Plugins   : cordova-plugin-ionic-keyboard 2.2.0, (and 43 other plugins)
                                                                                                                                                                                                                  
Utility:
                                                                                                                                                                                                                  
   cordova-res : not installed globally
   native-run  : 2.0.1
                                                                                                                                                                                                                  
System:
                                                                                                                                                                                                                  
   ios-deploy : 1.12.2
   ios-sim    : ios-sim/9.0.0 darwin-arm64 node-v20.11.1
   NodeJS     : v20.11.1 (/Users/mzb0005/.anyenv/envs/nodenv/versions/20.11.1/bin/node)
   npm        : 10.2.4
   OS         : macOS Unknown
   Xcode      : Xcode 15.2 Build version 15C500b

Additional Information

Cause of the Behavior

The issue arises because iOS VoiceOver triggers a focusin event when selecting an element. This event bubbles up, causing the focusin event of the ion-input to be fired subsequent to the clear input button's focusin event. Consequently, the input receives focus again as part of the callback function of the ion-input, which leads to the focus unexpectedly returning.

Suggested Fix

The current implementation compares document.activeElement with the element to be focused. To address the issue, it is advisable to also check the class of document.activeElement. Specifically, if the classList of the element contains input-clear-icon, which identifies the clear input button, then focus should not be shifted away from it. This adjustment will ensure that focus remains on the clear input button as intended.

@ionitron-bot ionitron-bot bot added the triage label Apr 18, 2024
@nagashimam nagashimam changed the title bug: ion-input with clearInput set traps VoiceOver's focus within itsself bug: unintended re-focus to ion-input after selecting clear input button with iOS VoiceOver Apr 18, 2024
@nagashimam nagashimam changed the title bug: unintended re-focus to ion-input after selecting clear input button with iOS VoiceOver bug: unintended re-focus to input after selecting clear input button with iOS VoiceOver Apr 18, 2024
@liamdebeasi liamdebeasi added package: core @ionic/core package type: bug a confirmed bug report labels Apr 19, 2024
@ionitron-bot ionitron-bot bot removed the triage label Apr 19, 2024
@liamdebeasi
Copy link
Contributor

Thanks for the report. Here is a dev build with a proposed fix if you are interested in testing: 8.0.1-dev.11713535425.1a4afba3

@liamdebeasi liamdebeasi removed their assignment Apr 19, 2024
@nagashimam
Copy link
Author

nagashimam commented Apr 19, 2024

@liamdebeasi
Thanks for the response and yes, stop propagation is much simpler approach! The problem is fixed with the version. StackBlitz has some issue installing dev builds as you said in another issue, but in my local project it works as intended.

@nagashimam
Copy link
Author

@liamdebeasi
Also, I understand that updates are primarily targeted at the latest versions, but I was wondering if there might be any possibility of backporting this fix to 7.x.x as well? Our team, along with quite a few others I believe, isn't quite ready to make the jump to 8.x.x just yet, while this fix is essential for screen reader users(especially those who are blind).

Thanks for considering this and for all the hard work you put into Ionic!

github-merge-queue bot pushed a commit that referenced this issue Apr 23, 2024
Issue number: resolves #29358

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

When the clear button is focused, `focusin` is dispatched and bubbles up
to the `ion-input`. Our [scroll assist utility listens for
`focusin`](https://github.com/ionic-team/ionic-framework/blob/2fc81ddc9b35d6909fd4b585079aedabbd659233/core/src/utils/input-shims/hacks/scroll-assist.ts#L135)
to adjust the scroll position. It also causes the input to be
re-focused. As a result, when swiping to the clear button with a screen
reader, focus will be forcibly moved back to the input. This means that
users cannot swipe away from the input to the right when using a screen
reader.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- To fix this, I decided to have the `focusin` event from the clear
button not bubble (as opposed to add a really specific workaround to the
scroll assist utility).

Adding `stopPropagation` was easy enough, but it turned out that the
scroll assist listeners were all configured to listen during the capture
phase instead of the bubble phase. As a result, `stopPropgation` had no
effect because the scroll assist callback had already fired. To address
this, I updated the listeners to listen during the bubbling phase
instead of the capture phase. Based on my testing the capture phase was
not required for scroll assist to work, so it appears safe to remove.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `8.0.1-dev.11713535425.1a4afba3`

Reviewers: Please test this on a physical iOS device and be sure to test
the scroll assist behavior. There is a test at
http://localhost:3333/src/utils/input-shims/hacks/test you can use.
@liamdebeasi
Copy link
Contributor

Thanks for the issue. This has been resolved via #29366, and a fix will be available in an upcoming release of Ionic Framework. We also plan to backport this to Ionic v7.

liamdebeasi added a commit that referenced this issue Apr 23, 2024
Issue number: resolves #29358

---------

<!-- Please do not submit updates to dependencies unless it fixes an
issue. -->

<!-- Please try to limit your pull request to one type (bugfix, feature,
etc). Submit multiple pull requests if needed. -->

## What is the current behavior?
<!-- Please describe the current behavior that you are modifying. -->

When the clear button is focused, `focusin` is dispatched and bubbles up
to the `ion-input`. Our [scroll assist utility listens for
`focusin`](https://github.com/ionic-team/ionic-framework/blob/2fc81ddc9b35d6909fd4b585079aedabbd659233/core/src/utils/input-shims/hacks/scroll-assist.ts#L135)
to adjust the scroll position. It also causes the input to be
re-focused. As a result, when swiping to the clear button with a screen
reader, focus will be forcibly moved back to the input. This means that
users cannot swipe away from the input to the right when using a screen
reader.

## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->

- To fix this, I decided to have the `focusin` event from the clear
button not bubble (as opposed to add a really specific workaround to the
scroll assist utility).

Adding `stopPropagation` was easy enough, but it turned out that the
scroll assist listeners were all configured to listen during the capture
phase instead of the bubble phase. As a result, `stopPropgation` had no
effect because the scroll assist callback had already fired. To address
this, I updated the listeners to listen during the bubbling phase
instead of the capture phase. Based on my testing the capture phase was
not required for scroll assist to work, so it appears safe to remove.

## Does this introduce a breaking change?

- [ ] Yes
- [x] No

<!--
  If this introduces a breaking change:
1. Describe the impact and migration path for existing applications
below.
  2. Update the BREAKING.md file with the breaking change.
3. Add "BREAKING CHANGE: [...]" to the commit description when merging.
See
https://github.com/ionic-team/ionic-framework/blob/main/docs/CONTRIBUTING.md#footer
for more information.
-->


## Other information

<!-- Any other information that is important to this PR such as
screenshots of how the component looks before and after the change. -->

Dev build: `8.0.1-dev.11713535425.1a4afba3`

Reviewers: Please test this on a physical iOS device and be sure to test
the scroll assist behavior. There is a test at
http://localhost:3333/src/utils/input-shims/hacks/test you can use.
@nagashimam
Copy link
Author

Thank you, it helps a lot!

@nagashimam
Copy link
Author

nagashimam commented May 16, 2024

@liamdebeasi
I failed to consider we have two render functions for ion-input in v7.8.6, renderInput and renderLegacyInput. We have put the fix inside renderInput, but not inside renderLegacyInput. Can I make a PR to add the same focusin event to this line?

@liamdebeasi
Copy link
Contributor

Hey there! I don't work for Ionic anymore, but you could bring it up with the team. I will note that the legacy input has been deprecated for over a year in v7, so I'm not sure what the team's appetite for backporting this fix to the legacy input will be. cc @brandyscarney

Copy link

ionitron-bot bot commented Jun 16, 2024

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Ionic, please create a new issue and ensure the template is fully filled out.

@ionitron-bot ionitron-bot bot locked and limited conversation to collaborators Jun 16, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.