mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2025-08-19 03:32:21 +08:00
fix(overlays): ensure that only topmost overlay is announced by screen readers (#28997)
Issue number: resolves #23472
---------
<!-- 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. -->
If multiple overlays are presented at the same time, none of them
receive `aria-hidden="true"`. This means that screen readers can read
contents from overlays behind the current one, which can be confusing
for users.
The original issue also reports router outlets getting `aria-hidden`
removed when any overlay is dismissed, not just the last one, but we've
since fixed that:
35ab6b4816/core/src/utils/overlays.ts (L573-L576)
## What is the new behavior?
<!-- Please describe the behavior or changes that are being added by
this PR. -->
All overlays besides the topmost one now receive `aria-hidden="true"`.
This means that screen readers will only announce the topmost overlay.
## 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/.github/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. -->
This commit is contained in:
@ -491,6 +491,16 @@ export const present = async <OverlayPresentOptions>(
|
||||
|
||||
setRootAriaHidden(true);
|
||||
|
||||
/**
|
||||
* Hide all other overlays from screen readers so only this one
|
||||
* can be read. Note that presenting an overlay always makes
|
||||
* it the topmost one.
|
||||
*/
|
||||
if (doc !== undefined) {
|
||||
const presentedOverlays = getPresentedOverlays(doc);
|
||||
presentedOverlays.forEach((o) => o.setAttribute('aria-hidden', 'true'));
|
||||
}
|
||||
|
||||
overlay.presented = true;
|
||||
overlay.willPresent.emit();
|
||||
overlay.willPresentShorthand?.emit();
|
||||
@ -528,6 +538,15 @@ export const present = async <OverlayPresentOptions>(
|
||||
if (overlay.keyboardClose && (document.activeElement === null || !overlay.el.contains(document.activeElement))) {
|
||||
overlay.el.focus();
|
||||
}
|
||||
|
||||
/**
|
||||
* If this overlay was previously dismissed without being
|
||||
* the topmost one (such as by manually calling dismiss()),
|
||||
* it would still have aria-hidden on being presented again.
|
||||
* Removing it here ensures the overlay is visible to screen
|
||||
* readers.
|
||||
*/
|
||||
overlay.el.removeAttribute('aria-hidden');
|
||||
};
|
||||
|
||||
/**
|
||||
@ -625,6 +644,15 @@ export const dismiss = async <OverlayDismissOptions>(
|
||||
}
|
||||
|
||||
overlay.el.remove();
|
||||
|
||||
/**
|
||||
* If there are other overlays presented, unhide the new
|
||||
* topmost one from screen readers.
|
||||
*/
|
||||
if (doc !== undefined) {
|
||||
getPresentedOverlay(doc)?.removeAttribute('aria-hidden');
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
Reference in New Issue
Block a user