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: interactive items in ion-item unexpectedly being focused with delegatesFocus #21982

Closed
larsblumberg opened this issue Aug 26, 2020 · 19 comments
Labels
package: core @ionic/core package type: bug a confirmed bug report

Comments

@larsblumberg
Copy link

larsblumberg commented Aug 26, 2020

Bug Report

Ionic version:
[ ] 4.x
[x] 5.x

Current behavior:
On Safari in macOS or iOS, IonInput (ion-input) steals focus when its value gets changed programmatically.

Expected behavior:

ion-input doesn't take the focus.

Steps to reproduce:
The following code snippet reproduces the effect that when an ion-input’s value got changed progammatically, here via a button click, then the ion-input will receive focus automatically as an effect. This effect is only observable within an ion-item container.

Apart from the fact that this might be unwanted and thus should be controllable by the programmer, it also comes with the side effect of the virtual keyboard popping up since the input receives focus. That’s only acceptable if the user sets focus explicitely on the input. However in this case, the input surprisingly "steals" the focus.

Related code:

const Test = () => {
  const [value, setValue] = useState("")
  return <IonItem>
    <IonInput value={value} onIonChange={e => setValue(e.detail.value)} />
    <IonButton onClick={() => setValue("foo")}>foo</IonButton>
  </IonItem>
}

Full demo project available here: https://github.com/larsblumberg/ion-input-focus-stealing-on-safari

Other information:

Effect only observable when input is contained within an ion-item and only in Safari on macOS and iOS.

Ionic info:

Ionic:
   Ionic CLI       : 6.11.1 (/Users/lars/.config/yarn/global/node_modules/@ionic/cli)
   Ionic Framework : @ionic/react 5.2.2

Capacitor:
   Capacitor CLI   : 2.0.2
   @capacitor/core : 2.2.0

Utility:
   cordova-res : not installed
   native-run  : not installed

System:
   NodeJS : v14.4.0 (/usr/local/Cellar/node/14.4.0/bin/node)
   npm    : 6.14.4
   OS     : macOS Catalina
@ionitron-bot ionitron-bot bot added the triage label Aug 26, 2020
@liamdebeasi
Copy link
Contributor

Thanks for the issue. Can you reproduce this in a blank Ionic starter app and provide a link to the GitHub repo?

@liamdebeasi liamdebeasi added the ionitron: needs reproduction a code reproduction is needed from the issue author label Aug 26, 2020
@ionitron-bot
Copy link

ionitron-bot bot commented Aug 26, 2020

Thanks for the issue! This issue has been labeled as needs reproduction. This label is added to issues that need a code reproduction.

Please provide a reproduction with the minimum amount of code required to reproduce the issue. Without a reliable code reproduction, it is unlikely we will be able to resolve the issue, leading to it being closed.

For a guide on how to create a good reproduction, see our Contributing Guide.

@ionitron-bot ionitron-bot bot removed the triage label Aug 26, 2020
@larsblumberg larsblumberg changed the title bug: ion-input steals focus (within ion-item) when its value got changed programmatically bug: ion-input steals focus in Safari (within ion-item) when its value got changed programmatically Aug 26, 2020
@larsblumberg
Copy link
Author

Here's a full Ionic app which reproduces this issue on Safari in macOS and iOS: https://github.com/larsblumberg/ion-input-focus-stealing-on-safari

@larsblumberg
Copy link
Author

@liamdebeasi If you have any workaround in mind that I could apply, that would be helpful. Due to this issue our iOS app has this awkward behavior that the virtual keyboard opens when the button is tapped (which changes an input value).

@larsblumberg larsblumberg changed the title bug: ion-input steals focus in Safari (within ion-item) when its value got changed programmatically bug: ion-input (contained in an ion-item) steals focus in Safari when its value got changed programmatically Aug 28, 2020
@liamdebeasi
Copy link
Contributor

liamdebeasi commented Sep 11, 2020

Apologies for the delay. This appears to be related to #22037 (same underlying issue). We have a PR open here: #22049 Though it needs to be updated to address buttons in addition to inputs.

The reason this is happening is ion-item has a Shadow DOM property call "delegatesFocus" set. Whenever ion-item receives focus, it will forward that focus to the first focusable element. In this case, clicking the button causes ion-item to receive focus, resulting in the focus being forwarded to the input.

@larsblumberg
Copy link
Author

thanks for the update

@larsblumberg
Copy link
Author

larsblumberg commented Sep 25, 2020

I've upgrade this issue's demo app to @ionic/react v. 5.3.3 as its release document states that the other, related issue #22037 (linked by @liamdebeasi) addresses the same underlying bug. However, the behavior I'm describing here isn't fixed and it's still an issue (bug).

@liamdebeasi
Copy link
Contributor

liamdebeasi commented Sep 25, 2020

I can reproduce. Looks like we need to account for other focusable elements besides ion-input and ion-textarea as well. cc @brandyscarney

@liamdebeasi liamdebeasi reopened this Sep 25, 2020
@liamdebeasi liamdebeasi added package: core @ionic/core package type: bug a confirmed bug report and removed ionitron: needs reproduction a code reproduction is needed from the issue author labels Sep 25, 2020
@brandyscarney
Copy link
Member

@liamdebeasi So this was definitely introduced by 5.1.1 aka adding delegatesFocus to item in order to fix tabindex not properly being passed down, but I don't think it's related to the code here:

private getFirstInput(): HTMLIonInputElement | HTMLIonTextareaElement {
const inputs = this.el.querySelectorAll('ion-input, ion-textarea') as NodeListOf<HTMLIonInputElement | HTMLIonTextareaElement>;
return inputs[0];
}
// This is needed for WebKit due to a delegatesFocus bug where
// clicking on the left padding of an item is not focusing the input
// but is opening the keyboard. It will no longer be needed with
// iOS 14.
@Listen('click')
delegateFocus(ev: Event) {
const clickedItem = (ev.target as HTMLElement).tagName === 'ION-ITEM';
const input = this.getFirstInput();
let firstActive = false;
// If the first input is the same as the active element we need
// to focus the first input again, but if the active element
// is another input inside of the item we shouldn't switch focus
if (input && document.activeElement) {
firstActive = input.querySelector('input, textarea') === document.activeElement;
}
// Only focus the first input if we clicked on an ion-item
// and the first input exists
if (clickedItem && input && firstActive) {
input.fireFocusEvents = false;
input.setBlur();
input.setFocus();
raf(() => {
input.fireFocusEvents = true;
});
}
}

That code was added because the input was "kind of" getting focus but not really - the keyboard was opening when the item was pressed but the input didn't have focus. So that just made it so the input actually received the focus it was supposed to have.

In my testing of this issue, the input is getting focus only on the first button click, only when the value changes, and only in Safari. This always evaluates to false since the event target is the ion-button so it isn't actually hitting this at all:

const clickedItem = (ev.target as HTMLElement).tagName === 'ION-ITEM';

Has this been tested with iOS 14? I think it may be another webkit bug with delegatesFocus.

Let me know if I'm misunderstanding this!

@larsblumberg
Copy link
Author

Has this been tested with iOS 14?

On iOS 14.0 the bug still manifests.

As well as on MacOS, Safari 14.0

@xclusive36
Copy link

xclusive36 commented Nov 27, 2020

To force ion-input to not steal focus,
Set ion-button with attribute tabIndex="1",
Set ion-input with attribute tabIndex="2"

<IonItem> <IonInput tabIndex={2} value={value} onIonChange={e => setValue(e.detail.value)} /> <IonButton tabIndex={1} onClick={() => setValue("foo")}>foo</IonButton> </IonItem>

As a side effect, the ion-button will take on a border when focused.
Add to css to remove the border.

ion-button:focus{ outline: none; }

@arnotixe
Copy link

arnotixe commented Jan 7, 2021

Same here, angular 10, capacitor+ionic5. Clicklistener on ion-avatar silently fails (is not trigged) when sticking an ion-input into the same ion-item as the ion-avatar. Screenshot:
image

@ghost
Copy link

ghost commented May 3, 2021

Running Ionic React v5.5.4 and am still seeing this issue with textareas.

I have a page with a text input and a text area, and clicking between the two items I see the focus jump back to the textarea. It almost looks like there is a delay in the focus in the textarea

@ghwilley
Copy link

ghwilley commented Jan 13, 2022

Running Ionic Angular 5.4.16 and seeing this issue with ion-inputs. It's within an ion-reorder-group, so there's no good way to avoid using ion-items.

Edit: I ended up getting around this by adding tabIndex to the elements that should keep focus.

@Suxsem
Copy link

Suxsem commented Jan 27, 2022

@liamdebeasi any news? thank you!

@liamdebeasi liamdebeasi changed the title bug: ion-input (contained in an ion-item) steals focus in Safari when its value got changed programmatically bug: interactive items in ion-item unexpectedly being focused with delegatesFocus Jul 28, 2023
@liamdebeasi liamdebeasi added type: bug a confirmed bug report and removed type: bug a confirmed bug report labels Jul 28, 2023
@grantmk
Copy link

grantmk commented Aug 29, 2023

Same issue ion Mac / Safari. Due to following structure, clicking anywhere in the content area would snap focus to the input. Also meant user couldn't select and copy text in the content area (and we know that users hate being restricted from using your app in ways you didn't intend...)

<ion-reorder-group>
  <ion-item>
    <ion-card>
      <ion-input>
      <content>. <!-- Clicking anywhere in here (including buttons) snaps focus to ion-input above -->

Solved this by adding tabindex="0" to the container card:

<ion-card tabindex="0">

The only difference for the user is that keyboard tabs now include the whole card on the way to actual inputs. Can't foresee any other issues but happy to be corrected if there are.

@alexbainbridge
Copy link

Another workaround is change ion-item to a div with display:flex

liamdebeasi added a commit that referenced this issue Mar 6, 2024
resolves #21982

BREAKING CHANGE:

- Item no longer automatically delegates focus to the first focusable element. While most developers should not need to make any changes to account for this update, usages of `ion-item` with interactive elements such as form controls (inputs, textareas, etc) should be evaluated to verify that interactions still work as expected.
@liamdebeasi
Copy link
Contributor

Thanks for the issue. This has been resolved via #29091, and a fix will be available in an upcoming major release of Ionic Framework.

Copy link

ionitron-bot bot commented Apr 5, 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 Apr 5, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
package: core @ionic/core package type: bug a confirmed bug report
Projects
None yet
Development

No branches or pull requests

9 participants