0

I am looking for a way to make JAWS (and probably other screen readers) announce the unfocused element. I am having a contenteditable area which after typing certain characters opens autocomplete/suggestion panel. The focus stays inside contenteditable to enable further typing while the panel is open showing possible suggestion. The JAWS does not announce the panel appearance because it is not focused. If the focus is forced on the panel JAWS announces it correctly (role="listbox") but of course it breaks the autocomplete functionality.

I created a very simple sample which illustrates the problem:

var editable = document.querySelector('#editable'),
  list = document.querySelector('#list'),
  counter = 0;

editable.addEventListener('keypress', function(evt) {
  counter++;
  if (counter > 5) {
    list.style.display = 'block';
    list.setAttribute("aria-expanded", true);
    // Focusing list works fine and JAWS announces everything correctly, but then the contenteditable loses focus and typing cannot be continued which is crucial for autocompleting functionality.
    setTimeout( function() { list.focus() }, 0 ); // Comment out this line to see/hear the difference.
  }
});
div {
  width: 400px;
  height: 300px;
  border: 1px solid #000;
}

.autocomplete_panel {
  position: absolute;
  display: none;
  box-sizing: border-box;
  width: 200px;
  max-height: 300px;
  overflow: auto;
  padding: 0;
  margin: 0;
  list-style: none;
  background: #FFF;
  border: 1px solid #b6b6b6;
  border-bottom-color: #999;
  border-radius: 3px;
  font: 12px Arial, Helvetica, Tahoma, Verdana, Sans-Serif;
}

#label_00 {
  display: none;
}
<div role="application">

  <div contenteditable="true" id="editable">
    Type sth to show panel...
  </div>

  <ul id="list" tabindex="-1" role="listbox" aria-labelledby="label_00" class="autocomplete_panel" style="z-index: 9997; left: 150px; top: 20px;">
    <li data-id="1" role="option" aria-posinset="1" aria-setsize="4">Item 1</li>
    <li data-id="2" role="option" aria-posinset="2" aria-setsize="4">Item 2</li>
    <li data-id="3" role="option" aria-posinset="3" aria-setsize="4">Item 3</li>
    <li data-id="4" role="option" aria-posinset="4" aria-setsize="4">Item 4</li>
    <li aria-hidden="true" id="label_00">Autosuggestion panel</li>
  </ul>

</div>

JSfiddle version: https://jsfiddle.net/f1ames/op15mm8z/.

Some solutions I am considering but are not perfect:

  1. Use hidden span with aria-live and dynamic text (with similar/same text to native JAWS announcement):

    • inconsistency between screen readers (different announcements for listbox in different screen readers)
    • won't be based on screen reader language (it should)
    • not flexible (JAWS may have different setting so announcements may vary - native JAWS announcement would be much better)
  2. Focusing autocomplete panel:

    • should still support typing inside contenteditable which may be very tricky especially for some unicode characters and composition
    • when focus gets back to contenteditable the announcement of autocomplete will be aborted
  3. Providing aria-markup to make JAWS properly announce autocomplete panel:

    • aria-live and aria-atomic works to some extend but the role is not supported. JAWS reads only aria-labelledby element or list items as plain text (difference for aria-atomic='true/false')

So at the moment I am not aware of any method to make JAWS announce the unfocused element same way as if it was focused. Any thoughts?

Btw. aria practices for autocomplete: https://www.w3.org/TR/wai-aria-practices/#autocomplete.

f1ames
  • 1,714
  • 1
  • 19
  • 36

1 Answers1

0

You need to use two aria attributes to achieve this.
1. aria-owns
   This attribute should have id of the list box, which should have role="listbox"
2. aria-activedescendant
   This attribute should have id of the list item inside the list box, which should have role="option"


Sample Code:
https://github.com/senthilshanmugam/a11yTypeAhead
Demo:
http://a11ywidgetsdemo.azurewebsites.net/#/a11yTypeAhead



In the below code sample, the attribute, aria-owns is having the id (AutoSuggestion-list) of the list box with role="listbox" and aria-activedescendant is having the id (AutoSuggestion-item-1) of the list item inside the list box with role="option". This item is the currently selected item as indicated by class="selected" attribute

When the user changes the selection the value in aria-activedescendant should point to the appropriate id of the list item, say AutoSuggestion-item-2

<input id="auto-complete" type="text" aria-expanded="true" 
  aria-labelledby="List-label" aria-owns="AutoSuggestion-list" 
  aria-activedescendant="AutoSuggestion-item-1">

<ul id="AutoSuggestion-list" tabindex="-1" role="listbox">
  <li id="AutoSuggestion-item-0" role="option" aria-label="Malaysia">Malaysia</li>
  <li id="AutoSuggestion-item-1" role="option" class="selected" aria-label="Maldives">Maldives</li>
  <li id="AutoSuggestion-item-2" role="option" aria-label="Myanmar (Burma)">Myanmar (Burma)</li>
  <li id="AutoSuggestion-item-3" role="option" aria-label="Oman">Oman</li>
</ul>