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

Queue messages sent to LiveAnnouncer #17852

Open
yurii-shylov opened this issue Dec 2, 2019 · 5 comments
Open

Queue messages sent to LiveAnnouncer #17852

yurii-shylov opened this issue Dec 2, 2019 · 5 comments

Comments

@yurii-shylov
Copy link

@yurii-shylov yurii-shylov commented Dec 2, 2019

Feature Description

LiveAnnouncer should be able to queue multiple messages and announce them one by one instead of announcing only the lates message.

Use Case

See https://stackblitz.com/edit/angular-b4jomd, it has a button which announces two messages "Table Foo was updated" and "Table Bar was updated" on click. It illustrates two use cases:

  1. Simultaneous announcements. Current implementation of LiveAnnouncer uses a single DOM element for the announcement, so second message overrides the first one and screen reader users hear only the second message.

  2. Consequent announcement of the same message. Current implementation will announce only distinct messages, i.e. clicking the button multiple times will produce only one message for the screen reader. That's not always convenient. For example, if you have a table on the page and you do some polling which updates the table 5 times, we need to be able to announce every of those updates so that screen reader users know what's happening on the page.

Implementation

We already have an implementation of a queued LiveAnnouncer which was used in GCP for the last 5 months, it alowed to fix a lot of accessibility problems on pages with heavy UIs. I will convert it to a PR here, but I'd like to have some discussion first. At the very least I'd like to know whether it should replace the existing LiveAnnouncer implementation or we should allow users to switch between two implementations

@yurii-shylov yurii-shylov added the feature label Dec 2, 2019
@jelbourn jelbourn added G P4 labels Dec 2, 2019
@jelbourn

This comment has been minimized.

Copy link
Member

@jelbourn jelbourn commented Dec 2, 2019

In general I think this could be a good improvement. It would be great to see a specific proposal/design doc for updating the existing LiveAnnouncer, since we would want to avoid any breaking changes.

@jelbourn jelbourn added the discussion label Dec 2, 2019
@yurii-shylov

This comment has been minimized.

Copy link
Author

@yurii-shylov yurii-shylov commented Dec 3, 2019

How current LiveAnnouncer works

In constructor LiveAnnouncer creates an aria-live <div>. Whenever user calls announce('Foo') it erases the content of that <div> and after a 100ms delay sets 'Foo' to it. Screen readers detect that change and read 'Foo' to the user.

If announce('Foo') and announce('Bar') are called approximatelly at the same time, second message overrides the value of that single <div> element. It means that screen reader will only read 'Bar'.

What QueuedLiveAnnouncer would change

There will be a TaskQueue class with a single method enqueue(task: () => void, politeness: AriaLivePoliteness, delay = 0): void. All tasks will be added to a single queue, they will be sorted by politeness only (so 'assertive' messages will always get announced before 'polite' ones, even if they were scheduled later). delay will regulate the delay before task gets executed (similar to the 100ms described above). So calling enqueue(foo, 100); enqueu(bar, 100) will execute task foo in ~100ms from now and task bar in ~200ms from now.

In constructor QueuedLiveAnnouncer creates only the TaskQueue. Whenever user calls announce('Foo') it creates a new <div> element with appropriate aria-live and attaches it to the DOM. It places a task to the queue with a 100ms delay; the task sets 'Foo' to the previously created <div> and schedules a removal of that <div> in 1000ms (originally we had 30s, but we've tested 1s in various screen readers, it's safe to use and allows to clean up DOM sooner). Screen readers detect new <div> and read 'Foo' to the user.

If announce('Foo') and announce('Bar') are called approximatelly at the same time, both divs will also be attached to the DOM at the same time, but the innex test for them will be set with a guaranteed difference of at least 100ms. This allows screen readers to properly read both 'Foo' and 'Bar' one by one. Unlike regular LiveAnnounce it also allows to read 'Foo' more than once because different divs are used for them.

Potential breakages

I see two potential problems:

  1. Some projects might actually depend on the fact, that currently LiveAnnouncer announces only distinct messages. They might want the behavior that calling announce('Foo'); announce('Foo'); announce('Foo'); announce('Bar'); produces only 2 messages.
  2. Since QueuedLiveAnnouncer adds a new DOM element for every message it incurs slightly worse performance than having a single element for all the messages.
@jelbourn

This comment has been minimized.

Copy link
Member

@jelbourn jelbourn commented Dec 3, 2019

  • I don't think I would want to create a separate class vs. changing the current LiveAnnouncer class. Having multiple API surfaces can be confusing for users.
  • We also wouldn't want to change the existing behavior of announce because some applications may depend on this. One possible approach would be to add a new method to LiveAnnouncer, enqueueAnnouncement or announceWithQueue
  • Which screen-reader + browser combinations have you tested? From experience, IE & Edge with JAWS has been the most difficult to support, and I would want to make sure this would work in all of
@yurii-shylov

This comment has been minimized.

Copy link
Author

@yurii-shylov yurii-shylov commented Dec 3, 2019

  • I agree that having an extra class would only complicate things. (Although personally I would be happy to split LiveAnnouncer into an interface and a single implementation. It's likely due to my java background, we always do it this way, but it has certain benefits. For example, when we were implementing QueuedLiveAnnouncer, we wanted to guarantee that its interface is the same. It means that we had to extend LiveAnnouncer and it means that we had to reuse its constructor, which creates a <div> element we do not need. Having an interface would automatically solve that issue)
  • I understand that we want to keep current announce untouched. I do not think that having an extra announceWithQueue is the solution though. First of all, it provides similar confusion as a separate class would do. Secondly, personally I was surprised that announce('Foo'); announce('Bar') actually announces only the second message, as a user who's seen LiveAnnounce for the first time I would expect that it handles that situation. Imho, announceWithQueue should be the primary method, but it sounds secondary comparing to just the announce one. What if we add a boolean config field to LiveAnnouncerDefaultOptions which will control whether queue is used or not? It will be false by default and people will have opportunity to switch it to true for their whole project without changing the way they call LiveAnnouncer.
  • Personally I tested with VoiceOver+Chrome, NVDA+Chrome and JAWS+Chrome, our testing team probably used more different combinations, but I doubt they used IE. When I'm at the PR stage, I'll make sure to test Firefox, IE and Edge as well.
@jelbourn

This comment has been minimized.

Copy link
Member

@jelbourn jelbourn commented Dec 3, 2019

I like the idea of using LiveAnnouncerDefaultOptions for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.