From 2dea6071db12903f2ce815328db19b95366aa9a5 Mon Sep 17 00:00:00 2001 From: Maria Hutt Date: Tue, 27 May 2025 09:08:16 -0700 Subject: [PATCH] fix(scroll-assist): allow focus on input's siblings (#30409) Co-authored-by: Shane --- core/src/components/input/input.tsx | 9 ------- .../utils/input-shims/hacks/scroll-assist.ts | 24 +++++++++++++++++++ 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx index 5f4b125714..afdaf6a79d 100644 --- a/core/src/components/input/input.tsx +++ b/core/src/components/input/input.tsx @@ -868,15 +868,6 @@ export class Input implements ComponentInterface { */ ev.preventDefault(); }} - onFocusin={(ev) => { - /** - * Prevent the focusin event from bubbling otherwise it will cause the focusin - * event listener in scroll assist to fire. When this fires, focus will be moved - * back to the input even if the clear button was never tapped. This poses issues - * for screen readers as it means users would be unable to swipe past the clear button. - */ - ev.stopPropagation(); - }} onClick={this.clearTextInput} > diff --git a/core/src/utils/input-shims/hacks/scroll-assist.ts b/core/src/utils/input-shims/hacks/scroll-assist.ts index 293f1d1d26..fb2b190020 100644 --- a/core/src/utils/input-shims/hacks/scroll-assist.ts +++ b/core/src/utils/input-shims/hacks/scroll-assist.ts @@ -181,6 +181,30 @@ const setManualFocus = (el: HTMLElement) => { return; } + /** + * Optimization for scenarios where the currently focused element is a sibling + * of the target element. In such cases, we avoid setting `SKIP_SCROLL_ASSIST`. + * + * This is crucial for accessibility: input elements can now contain focusable + * siblings (e.g., clear buttons, slotted elements). If we didn't skip setting + * the attribute here, screen readers would be unable to navigate to and interact + * with these sibling elements. + * + * Without this check, we would need to call `ev.stopPropagation()` on the + * 'focusin' event of each focusable sibling to prevent the scroll assist + * listener from incorrectly moving focus back to the input. That approach + * would be less maintainable and more error-prone. + */ + const inputId = el.getAttribute('id'); + const label = el.closest(`label[for="${inputId}"]`); + const activeElLabel = document.activeElement?.closest(`label[for="${inputId}"]`); + + if (label !== null && label === activeElLabel) { + // If the label is the same as the active element label, then + // we don't need to set the `SKIP_SCROLL_ASSIST` and reset focus. + return; + } + el.setAttribute(SKIP_SCROLL_ASSIST, 'true'); el.focus(); };