jQuery-Selectric icon indicating copy to clipboard operation
jQuery-Selectric copied to clipboard

"Blank" screen reader dropdown options

Open bewards opened this issue 9 years ago • 5 comments

Using opensource NVDA screen reader on selectric demo page, the options read out "blank" when using arrow keys.

The focus state looks like it's on the "option" markup, but using document.activeElement shows that the focus is on the <body> tag. I had an analysis of WCAG AA done, with the following results:

WCAG criteria fails the following sections: 1.3.1, 4.1.2 Description: Using Safari with Voiceover, Internet Explorer with JAWS or FireFox with NVDA, make a selection from the menu using the Down Arrow key (JAWS, NVDA) or Control+Option+Right Arrow (VO) and press control+option+space (VO) or Space (JAWS, NVDA):

  • The menu expands automatically when focused, instead of reading role=list and the currently selected option or the placeholder. I believe most accessible dropdowns for select tags operate by opening the list when enter is pressed.
  • It announces as "edit text blank main X items" or "blank" w/ NVDA
  • When the user uses the down arrow key, the dropdown moves but no announcement is made.
  • If the user presses the tab key after pressing the down arrow key, focus is shifted to the next tabular element.
  • If the user uses Control+Option+Right arrow (VO), the elements underneath the dropdown are focused.

Other than keyboard navigation, is there a possible fix for even just the screen reader reading "blank"? And has there been any talk of accessibility in the works for upcoming releases?

bewards avatar Jan 07 '17 19:01 bewards

Pardon my non-technical background of this plugin, but if a user is able to key through the custom html options, would it be possible to set focus on the highlighted option via tab-index or using aria-labeledby to read the highlighted option?

bewards avatar Jan 09 '17 16:01 bewards

Yes, accessibility is something that I would like very much to work on.

However, at the moment, my spare time is pretty limited and I can't work on the project as much as I wanted. By the way, sorry the long delay to answer.

Thank you for your insights. It will be very usefull when this functionality get implemented.

Pull requests are welcomed, if someone wants to work on this 😉

lcdsantos avatar Mar 08 '17 14:03 lcdsantos

We decided to implement proper aria properties for elements, and once done, contribute it back to the community. Feel free to assign this to me. Expect a PR within two weeks.

pszoldan avatar Oct 30 '17 19:10 pszoldan

We've made progress on this issue, but it's not ready for a PR yet. If anybody is interested in helping out with testing ( @bewards ? ) or even development, you can grab my fork here https://github.com/pszoldan/jQuery-Selectric

pszoldan avatar Nov 10 '17 21:11 pszoldan

Couldn't see any improvement with this PR. Ended up implementing ARIA compatibility, by using the API (using selectric 1.13)

      $('.block-facets').each(function (ind, elm) {

        // Aria interaction for selectric.
        // @see https://www.w3.org/TR/wai-aria-practices/examples/combobox/aria1.1pattern/listbox-combo.html#sc3_label
          var
            wrapper = $(elm),
            select_elm = wrapper.find('.facets-dropdown'),
            id_prefix = select_elm.attr('id') ? select_elm.attr('id') : getRandomId(),
            label_id = select_elm.attr('aria-labelledby'),
            listbox_id = id_prefix + '_listbox',
            text_input,
            listbox,
            combobox;

          select_elm.selectric({
            onInit: function (select, selectric) {
              text_input = wrapper.find('.selectric-input');
              text_input.attr('aria-labelledby', label_id);
            },
            onBeforeOpen: function (select, selectric) {
              listbox = wrapper.find('.selectric-scroll > ul');
              combobox = wrapper.find('.selectric');

              // Add fake :active.
              wrapper.addClass('is-focused');
              text_input
                .attr('aria-autocomplete','both')
                .attr('aria-controls',listbox_id)
                .attr('aria-labelledby', label_id)
                .attr('id', id_prefix + '_input')
                .attr('aria-activedescendant','');
              combobox
                .attr('role', 'combobox')
                .attr('aria-expanded', false)
                .attr('aria-owns', false)
                .attr('aria-owns', listbox_id)
                .attr('aria-haspopup', listbox_id)
                .attr('id', id_prefix + '_combobox')
                .attr('aria-expanded', false)
              listbox
                .attr('aria-labelledby', label_id)
                .attr('id', listbox_id);
            },
            onOpen: function () {
              combobox.attr('aria-expanded', true);
            },
            onClose: function () {
              combobox.attr('aria-expanded', false);
            },
            onBeforeClose: function (select, selectric) {
              wrapper.removeClass('is-focused')
            },
            onBeforeHighlight: function (select, selectric) {
              wrapper.find('[aria-selected]').removeAttr('aria-selected');
            },
            onHighlight: function (select, selectric) {
              wrapper.find('[data-index=' + selectric.state.highlightedIdx + ']')
                .find('[role="option"]')
                .attr('aria-selected', true);
              text_input.attr('aria-activedescendant',id_prefix + '_listbox_' + selectric.state.highlightedIdx);
            },
            optionsItemBuilder: function (itemData, element, index) {
              var unselect = (index == 0 && !element.selected) ? 'aria-label="' + Drupal.t('remove filter') + '"' : ''
              return '<span role="option" ' + unselect + ' id="' + id_prefix + '_listbox_' + index + '" >{text}</span>';
            }
          });
      })
    }
    
    
    function getRandomId() {
      var charSet = 'abcdefghijklmnopqrstuvwxyz';
      var randomString = '';
      for (var i = 0; i < 12; i++) {
        var randomPoz = Math.floor(Math.random() * charSet.length);
        randomString += charSet.substring(randomPoz, randomPoz + 1);
      }
      return 'facet_' + randomString;
    },

digitaldonkey avatar Jan 27 '21 10:01 digitaldonkey