fix(scroll-assist): allow focus on input's siblings (#30409)

Co-authored-by: Shane <shane@shanessite.net>
This commit is contained in:
Maria Hutt
2025-05-27 09:08:16 -07:00
committed by GitHub
parent b740070ca4
commit 2dea6071db
2 changed files with 24 additions and 9 deletions

View File

@ -868,15 +868,6 @@ export class Input implements ComponentInterface {
*/ */
ev.preventDefault(); 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} onClick={this.clearTextInput}
> >
<ion-icon aria-hidden="true" icon={clearIconData}></ion-icon> <ion-icon aria-hidden="true" icon={clearIconData}></ion-icon>

View File

@ -181,6 +181,30 @@ const setManualFocus = (el: HTMLElement) => {
return; 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.setAttribute(SKIP_SCROLL_ASSIST, 'true');
el.focus(); el.focus();
}; };