From 83a855f961c22d1f447f7844d5e38a525bd7521f Mon Sep 17 00:00:00 2001 From: Cooper Date: Tue, 26 Apr 2016 17:02:48 -0400 Subject: [PATCH] Add initial round of aria labeling to jQuery Related to harvesthq#264 Use the label element, aria-label, or aria-labelledby attributes from the select box for the text input field. Add attributes and ids such that a screen reader can know which element in the search results is highlighted so that it can be read: Attributes on the text input: aria-haspopup="true" role="combobox" aria-autocomplete="list" aria-owns="id_of_list_of_options" aria-activedescendant="id_of_highlighted_option" (change this value programatically when the highlighted option changes) aria-expanded="false" (changed dynamically as result select list is shown/hidden) Attributes on the list of options: id for use in the aria-owns attribute on the input role="listbox" @see https://www.w3.org/TR/wai-aria/roles#listbox Attributes on each option: role="option" id for use in the aria-activedescendant attribute on the input Inspired by https://github.com/kirstenmalin/chosen/commit/4d0da63e3839df229c3095e692395acbd39d811a but with some other minor additions --- coffee/chosen.jquery.coffee | 20 ++++++++++++++++++-- coffee/lib/abstract-chosen.coffee | 2 ++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/coffee/chosen.jquery.coffee b/coffee/chosen.jquery.coffee index fbabf9280fd..d59923bdcba 100644 --- a/coffee/chosen.jquery.coffee +++ b/coffee/chosen.jquery.coffee @@ -42,15 +42,14 @@ class Chosen extends AbstractChosen @container = ($ "
", container_props) if @is_multiple - @container.html '
    ' else - @container.html '' + @default_text + '
      ' @form_field_jq.hide().after @container @dropdown = @container.find('div.chosen-drop').first() @search_field = @container.find('input').first() @search_results = @container.find('ul.chosen-results').first() + @search_results.attr('id', "#{@form_field.id}-chosen-search-results") this.search_field_scale() @search_no_results = @container.find('li.no-results').first() @@ -62,6 +61,7 @@ class Chosen extends AbstractChosen @search_container = @container.find('div.chosen-search').first() @selected_item = @container.find('.chosen-single').first() + this.set_aria_labels() this.results_build() this.set_tab_index() this.set_label_behavior() @@ -113,6 +113,20 @@ class Chosen extends AbstractChosen @form_field_jq.removeData('chosen') @form_field_jq.show() + set_aria_labels: -> + @search_field.attr "aria-owns", @search_results.attr "id" + if @form_field.attributes["aria-label"] + @search_field.attr "aria-label", @form_field.attributes["aria-label"] + if @form_field.attributes["aria-labelledby"] + @search_field.attr "aria-labelledby", @form_field.attributes["aria-labelledby"] + else if @form_field.labels.length + labelledbyList = "" + for label, i in @form_field.labels + if label.id is "" + label.id = "#{@form_field.id}-chosen-label-#{i}" + labelledbyList += @form_field.labels[i].id + " " + @search_field.attr "aria-labelledby", labelledbyList + search_field_disabled: -> @is_disabled = @form_field_jq[0].disabled if(@is_disabled) @@ -213,6 +227,8 @@ class Chosen extends AbstractChosen @result_highlight = el @result_highlight.addClass "highlighted" + @search_field.attr("aria-activedescendant", @result_highlight.attr("id")) + maxHeight = parseInt @search_results.css("maxHeight"), 10 visible_top = @search_results.scrollTop() visible_bottom = maxHeight + visible_top diff --git a/coffee/lib/abstract-chosen.coffee b/coffee/lib/abstract-chosen.coffee index 92b91ba3475..63efe402a86 100644 --- a/coffee/lib/abstract-chosen.coffee +++ b/coffee/lib/abstract-chosen.coffee @@ -106,6 +106,8 @@ class AbstractChosen option_el.className = classes.join(" ") option_el.style.cssText = option.style option_el.setAttribute("data-option-array-index", option.array_index) + option_el.setAttribute("role", "option") + option_el.id = "#{@form_field.id}-chosen-search-result-#{option.array_index}" option_el.innerHTML = option.search_text option_el.title = option.title if option.title