Skip to content

Commit 9eb12a8

Browse files
committed
Merge pull request select2#1816 from funkjedi/accessibility
Accessibility updates based on jQuery UI Autocomplete
2 parents d5cb4bf + eaf09c7 commit 9eb12a8

3 files changed

Lines changed: 52 additions & 6 deletions

File tree

select2.css

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,17 @@ Version: @@ver@@ Timestamp: @@timestamp@@
297297
background-position: -18px 1px;
298298
}
299299

300+
.select2-hidden-accessible {
301+
border: 0;
302+
clip: rect(0 0 0 0);
303+
height: 1px;
304+
margin: -1px;
305+
overflow: hidden;
306+
padding: 0;
307+
position: absolute;
308+
width: 1px;
309+
}
310+
300311
/* results */
301312
.select2-results {
302313
max-height: 200px;

select2.js

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,13 @@ the specific language governing permissions and limitations under the Apache Lic
677677

678678
this.container = this.createContainer();
679679

680+
this.liveRegion = $("<span>", {
681+
role: "status",
682+
"aria-live": "polite"
683+
})
684+
.addClass("select2-hidden-accessible")
685+
.appendTo(document.body);
686+
680687
this.containerId="s2id_"+(opts.element.attr("id") || "autogen"+nextUid()).replace(/([;&,\-\.\+\*\~':"\!\^#$%@\[\]\(\)=>\|])/g, '\\$1');
681688
this.containerSelector="#"+this.containerId;
682689
this.container.attr("id", this.containerId);
@@ -763,7 +770,7 @@ the specific language governing permissions and limitations under the Apache Lic
763770
this.dropdown.on("click mouseup mousedown", function (e) { e.stopPropagation(); });
764771

765772
this.nextSearchTerm = undefined;
766-
773+
767774
if ($.isFunction(this.opts.initSelection)) {
768775
// initialize selection based on the current value of the source element
769776
this.initSelection();
@@ -791,7 +798,7 @@ the specific language governing permissions and limitations under the Apache Lic
791798
this.autofocus = opts.element.prop("autofocus");
792799
opts.element.prop("autofocus", false);
793800
if (this.autofocus) this.focus();
794-
801+
795802
},
796803

797804
// abstract
@@ -804,6 +811,7 @@ the specific language governing permissions and limitations under the Apache Lic
804811

805812
if (select2 !== undefined) {
806813
select2.container.remove();
814+
select2.liveRegion.remove();
807815
select2.dropdown.remove();
808816
element
809817
.removeClass("select2-offscreen")
@@ -861,7 +869,7 @@ the specific language governing permissions and limitations under the Apache Lic
861869

862870
opts = $.extend({}, {
863871
populateResults: function(container, results, query) {
864-
var populate, id=this.opts.id;
872+
var populate, id=this.opts.id, liveRegion=this.liveRegion;
865873

866874
populate=function(results, container, depth) {
867875

@@ -910,6 +918,8 @@ the specific language governing permissions and limitations under the Apache Lic
910918
node.data("select2-data", result);
911919
container.append(node);
912920
}
921+
922+
liveRegion.text(opts.formatMatches(results.length));
913923
};
914924

915925
populate(results, container, 0);
@@ -1484,6 +1494,8 @@ the specific language governing permissions and limitations under the Apache Lic
14841494

14851495
this.ensureHighlightVisible();
14861496

1497+
this.liveRegion.text(choice.text());
1498+
14871499
data = choice.data("select2-data");
14881500
if (data) {
14891501
this.opts.element.trigger({ type: "select2-highlight", val: this.id(data), choice: data });
@@ -1591,6 +1603,12 @@ the specific language governing permissions and limitations under the Apache Lic
15911603
function postRender() {
15921604
search.removeClass("select2-active");
15931605
self.positionDropdown();
1606+
if (results.find('.select2-no-results,.select2-selection-limit,.select2-searching').length) {
1607+
self.liveRegion.text(results.text());
1608+
}
1609+
else {
1610+
self.liveRegion.text(self.opts.formatMatches(results.find('.select2-result-selectable').length));
1611+
}
15941612
}
15951613

15961614
function render(html) {
@@ -1828,9 +1846,11 @@ the specific language governing permissions and limitations under the Apache Lic
18281846
" <span class='select2-chosen'>&nbsp;</span><abbr class='select2-search-choice-close'></abbr>",
18291847
" <span class='select2-arrow' role='presentation'><b role='presentation'></b></span>",
18301848
"</a>",
1849+
"<label for='' class='select2-offscreen'></label>",
18311850
"<input class='select2-focusser select2-offscreen' type='text' aria-haspopup='true' role='button' />",
18321851
"<div class='select2-drop select2-display-none'>",
18331852
" <div class='select2-search'>",
1853+
" <label for='' class='select2-offscreen'></label>",
18341854
" <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input' role='combobox' aria-expanded='true'",
18351855
" aria-autocomplete='list' />",
18361856
" </div>",
@@ -1960,7 +1980,10 @@ the specific language governing permissions and limitations under the Apache Lic
19601980
// rewrite labels from original element to focusser
19611981
this.focusser.attr("id", "s2id_autogen"+idSuffix);
19621982

1963-
elementLabel = $("label[for='" + this.opts.element.attr("id") + "']")
1983+
elementLabel = $("label[for='" + this.opts.element.attr("id") + "']");
1984+
1985+
this.focusser.prev()
1986+
.text(elementLabel.text())
19641987
.attr('for', this.focusser.attr('id'));
19651988

19661989
// Ensure the original element retains an accessible name
@@ -1969,6 +1992,13 @@ the specific language governing permissions and limitations under the Apache Lic
19691992

19701993
this.focusser.attr("tabindex", this.elementTabIndex);
19711994

1995+
// write label for search field using the label from the focusser element
1996+
this.search.attr("id", this.focusser.attr('id') + '_search');
1997+
1998+
this.search.prev()
1999+
.text($("label[for='" + this.focusser.attr('id') + "']").text())
2000+
.attr('for', this.search.attr('id'));
2001+
19722002
this.search.on("keydown", this.bind(function (e) {
19732003
if (!this.isInterfaceEnabled()) return;
19742004

@@ -2148,7 +2178,7 @@ the specific language governing permissions and limitations under the Apache Lic
21482178
self.nextSearchTerm = self.opts.nextSearchTerm(selected, self.search.val());
21492179
}
21502180
});
2151-
}
2181+
}
21522182
},
21532183

21542184
isPlaceholderOptionSelected: function() {
@@ -2410,6 +2440,7 @@ the specific language governing permissions and limitations under the Apache Lic
24102440
}).html([
24112441
"<ul class='select2-choices'>",
24122442
" <li class='select2-search-field'>",
2443+
" <label for='' class='select2-offscreen'></label>",
24132444
" <input type='text' autocomplete='off' autocorrect='off' autocapitalize='off' spellcheck='false' class='select2-input'>",
24142445
" </li>",
24152446
"</ul>",
@@ -2521,7 +2552,9 @@ the specific language governing permissions and limitations under the Apache Lic
25212552

25222553
// rewrite labels from original element to focusser
25232554
this.search.attr("id", "s2id_autogen"+nextUid());
2524-
$("label[for='" + this.opts.element.attr("id") + "']")
2555+
2556+
this.search.prev()
2557+
.text($("label[for='" + this.opts.element.attr("id") + "']").text())
25252558
.attr('for', this.search.attr('id'));
25262559

25272560
this.search.on("input paste", this.bind(function() {
@@ -3257,6 +3290,7 @@ the specific language governing permissions and limitations under the Apache Lic
32573290
},
32583291
formatResultCssClass: function(data) {return data.css;},
32593292
formatSelectionCssClass: function(data, container) {return undefined;},
3293+
formatMatches: function (matches) { return matches + " results are available, use up and down arrow keys to navigate."; },
32603294
formatNoMatches: function () { return "No matches found"; },
32613295
formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " or more character" + (n == 1? "" : "s"); },
32623296
formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1? "" : "s"); },

select2_locale_en.js.template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"use strict";
88

99
$.extend($.fn.select2.defaults, {
10+
formatMatches: function (matches) { return matches + " results are available, use up and down arrow keys to navigate."; },
1011
formatNoMatches: function () { return "No matches found"; },
1112
formatInputTooShort: function (input, min) { var n = min - input.length; return "Please enter " + n + " more character" + (n == 1 ? "" : "s"); },
1213
formatInputTooLong: function (input, max) { var n = input.length - max; return "Please delete " + n + " character" + (n == 1 ? "" : "s"); },

0 commit comments

Comments
 (0)