Accessibility matches better
Now the accessibility for the single select box better matches what is picked up for a native select. This fixes an issue with us always setting `aria-activedescendant`, which caused Orca to always say the full path to the selected result, instead of saying that the box was a combobox with a selection. This means that the `aria-activedescendant` attribute will now only be set when the dropdown is open. This also switches the results list from a listbox to a tree, which is what Firefox tells screen readers that a standard select is. Combined with a change to use `role=group` and `aria-label` for option group labels, screen readers will now announce the group that they are in as well.
This commit is contained in:
parent
7c8601d33b
commit
cdfa0499f4
4
dist/css/select2.css
vendored
4
dist/css/select2.css
vendored
@ -110,9 +110,9 @@
|
|||||||
.select2-container.select2-theme-default .dropdown .results > .options {
|
.select2-container.select2-theme-default .dropdown .results > .options {
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow-y: auto; }
|
overflow-y: auto; }
|
||||||
.select2-container.select2-theme-default .dropdown .results .options .option.group {
|
.select2-container.select2-theme-default .dropdown .results .options .option[role=group] {
|
||||||
padding: 0; }
|
padding: 0; }
|
||||||
.select2-container.select2-theme-default .dropdown .results .options .option.group .group-label {
|
.select2-container.select2-theme-default .dropdown .results .options .option[role=group] .group-label {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 6px; }
|
padding: 6px; }
|
||||||
|
2
dist/css/select2.min.css
vendored
2
dist/css/select2.min.css
vendored
File diff suppressed because one or more lines are too long
28
dist/js/select2.amd.full.js
vendored
28
dist/js/select2.amd.full.js
vendored
@ -163,7 +163,7 @@ define('select2/results',[
|
|||||||
|
|
||||||
Results.prototype.render = function () {
|
Results.prototype.render = function () {
|
||||||
var $results = $(
|
var $results = $(
|
||||||
'<ul class="options" role="listbox"></ul>'
|
'<ul class="options" role="tree"></ul>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.options.get('multiple')) {
|
if (this.options.get('multiple')) {
|
||||||
@ -236,12 +236,13 @@ define('select2/results',[
|
|||||||
|
|
||||||
Results.prototype.option = function (data) {
|
Results.prototype.option = function (data) {
|
||||||
var $option = $(
|
var $option = $(
|
||||||
'<li class="option" role="option" aria-selected="false"></li>'
|
'<li class="option" role="treeitem" aria-selected="false"></li>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.children) {
|
if (data.children) {
|
||||||
$option
|
$option
|
||||||
.addClass('group')
|
.attr('role', 'group')
|
||||||
|
.attr('aria-label', data.text)
|
||||||
.removeAttr('aria-selected');
|
.removeAttr('aria-selected');
|
||||||
|
|
||||||
var $label = $('<strong class="group-label"></strong>');
|
var $label = $('<strong class="group-label"></strong>');
|
||||||
@ -297,20 +298,32 @@ define('select2/results',[
|
|||||||
self.clear();
|
self.clear();
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('results:append', function (params) {
|
container.on('results:append', function (params) {
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('select', function () {
|
container.on('select', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('unselect', function () {
|
container.on('unselect', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -580,13 +593,14 @@ define('select2/selection/single',[
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('open', function () {
|
container.on('open', function () {
|
||||||
// When the dropdown is open, aria-expended="true"
|
// When the dropdown is open, aria-expanded="true"
|
||||||
self.$selection.attr('aria-expanded', 'true');
|
self.$selection.attr('aria-expanded', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('close', function () {
|
container.on('close', function () {
|
||||||
// When the dropdown is closed, aria-expended="false"
|
// When the dropdown is closed, aria-expanded="false"
|
||||||
self.$selection.attr('aria-expanded', 'false');
|
self.$selection.attr('aria-expanded', 'false');
|
||||||
|
self.$selection.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$selection.on('focus', function (evt) {
|
this.$selection.on('focus', function (evt) {
|
||||||
@ -655,10 +669,6 @@ define('select2/selection/single',[
|
|||||||
var formatted = this.display(selection);
|
var formatted = this.display(selection);
|
||||||
|
|
||||||
this.$selection.find('.rendered-selection').html(formatted);
|
this.$selection.find('.rendered-selection').html(formatted);
|
||||||
|
|
||||||
if (data[0]._resultId != null) {
|
|
||||||
this.$selection.attr('aria-activedescendant', data[0]._resultId);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return SingleSelection;
|
return SingleSelection;
|
||||||
|
28
dist/js/select2.amd.js
vendored
28
dist/js/select2.amd.js
vendored
@ -163,7 +163,7 @@ define('select2/results',[
|
|||||||
|
|
||||||
Results.prototype.render = function () {
|
Results.prototype.render = function () {
|
||||||
var $results = $(
|
var $results = $(
|
||||||
'<ul class="options" role="listbox"></ul>'
|
'<ul class="options" role="tree"></ul>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.options.get('multiple')) {
|
if (this.options.get('multiple')) {
|
||||||
@ -236,12 +236,13 @@ define('select2/results',[
|
|||||||
|
|
||||||
Results.prototype.option = function (data) {
|
Results.prototype.option = function (data) {
|
||||||
var $option = $(
|
var $option = $(
|
||||||
'<li class="option" role="option" aria-selected="false"></li>'
|
'<li class="option" role="treeitem" aria-selected="false"></li>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.children) {
|
if (data.children) {
|
||||||
$option
|
$option
|
||||||
.addClass('group')
|
.attr('role', 'group')
|
||||||
|
.attr('aria-label', data.text)
|
||||||
.removeAttr('aria-selected');
|
.removeAttr('aria-selected');
|
||||||
|
|
||||||
var $label = $('<strong class="group-label"></strong>');
|
var $label = $('<strong class="group-label"></strong>');
|
||||||
@ -297,20 +298,32 @@ define('select2/results',[
|
|||||||
self.clear();
|
self.clear();
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('results:append', function (params) {
|
container.on('results:append', function (params) {
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('select', function () {
|
container.on('select', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('unselect', function () {
|
container.on('unselect', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -580,13 +593,14 @@ define('select2/selection/single',[
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('open', function () {
|
container.on('open', function () {
|
||||||
// When the dropdown is open, aria-expended="true"
|
// When the dropdown is open, aria-expanded="true"
|
||||||
self.$selection.attr('aria-expanded', 'true');
|
self.$selection.attr('aria-expanded', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('close', function () {
|
container.on('close', function () {
|
||||||
// When the dropdown is closed, aria-expended="false"
|
// When the dropdown is closed, aria-expanded="false"
|
||||||
self.$selection.attr('aria-expanded', 'false');
|
self.$selection.attr('aria-expanded', 'false');
|
||||||
|
self.$selection.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$selection.on('focus', function (evt) {
|
this.$selection.on('focus', function (evt) {
|
||||||
@ -655,10 +669,6 @@ define('select2/selection/single',[
|
|||||||
var formatted = this.display(selection);
|
var formatted = this.display(selection);
|
||||||
|
|
||||||
this.$selection.find('.rendered-selection').html(formatted);
|
this.$selection.find('.rendered-selection').html(formatted);
|
||||||
|
|
||||||
if (data[0]._resultId != null) {
|
|
||||||
this.$selection.attr('aria-activedescendant', data[0]._resultId);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return SingleSelection;
|
return SingleSelection;
|
||||||
|
28
dist/js/select2.full.js
vendored
28
dist/js/select2.full.js
vendored
@ -9701,7 +9701,7 @@ define('select2/results',[
|
|||||||
|
|
||||||
Results.prototype.render = function () {
|
Results.prototype.render = function () {
|
||||||
var $results = $(
|
var $results = $(
|
||||||
'<ul class="options" role="listbox"></ul>'
|
'<ul class="options" role="tree"></ul>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.options.get('multiple')) {
|
if (this.options.get('multiple')) {
|
||||||
@ -9774,12 +9774,13 @@ define('select2/results',[
|
|||||||
|
|
||||||
Results.prototype.option = function (data) {
|
Results.prototype.option = function (data) {
|
||||||
var $option = $(
|
var $option = $(
|
||||||
'<li class="option" role="option" aria-selected="false"></li>'
|
'<li class="option" role="treeitem" aria-selected="false"></li>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.children) {
|
if (data.children) {
|
||||||
$option
|
$option
|
||||||
.addClass('group')
|
.attr('role', 'group')
|
||||||
|
.attr('aria-label', data.text)
|
||||||
.removeAttr('aria-selected');
|
.removeAttr('aria-selected');
|
||||||
|
|
||||||
var $label = $('<strong class="group-label"></strong>');
|
var $label = $('<strong class="group-label"></strong>');
|
||||||
@ -9835,20 +9836,32 @@ define('select2/results',[
|
|||||||
self.clear();
|
self.clear();
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('results:append', function (params) {
|
container.on('results:append', function (params) {
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('select', function () {
|
container.on('select', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('unselect', function () {
|
container.on('unselect', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -10118,13 +10131,14 @@ define('select2/selection/single',[
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('open', function () {
|
container.on('open', function () {
|
||||||
// When the dropdown is open, aria-expended="true"
|
// When the dropdown is open, aria-expanded="true"
|
||||||
self.$selection.attr('aria-expanded', 'true');
|
self.$selection.attr('aria-expanded', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('close', function () {
|
container.on('close', function () {
|
||||||
// When the dropdown is closed, aria-expended="false"
|
// When the dropdown is closed, aria-expanded="false"
|
||||||
self.$selection.attr('aria-expanded', 'false');
|
self.$selection.attr('aria-expanded', 'false');
|
||||||
|
self.$selection.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$selection.on('focus', function (evt) {
|
this.$selection.on('focus', function (evt) {
|
||||||
@ -10193,10 +10207,6 @@ define('select2/selection/single',[
|
|||||||
var formatted = this.display(selection);
|
var formatted = this.display(selection);
|
||||||
|
|
||||||
this.$selection.find('.rendered-selection').html(formatted);
|
this.$selection.find('.rendered-selection').html(formatted);
|
||||||
|
|
||||||
if (data[0]._resultId != null) {
|
|
||||||
this.$selection.attr('aria-activedescendant', data[0]._resultId);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return SingleSelection;
|
return SingleSelection;
|
||||||
|
4
dist/js/select2.full.min.js
vendored
4
dist/js/select2.full.min.js
vendored
File diff suppressed because one or more lines are too long
28
dist/js/select2.js
vendored
28
dist/js/select2.js
vendored
@ -592,7 +592,7 @@ define('select2/results',[
|
|||||||
|
|
||||||
Results.prototype.render = function () {
|
Results.prototype.render = function () {
|
||||||
var $results = $(
|
var $results = $(
|
||||||
'<ul class="options" role="listbox"></ul>'
|
'<ul class="options" role="tree"></ul>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.options.get('multiple')) {
|
if (this.options.get('multiple')) {
|
||||||
@ -665,12 +665,13 @@ define('select2/results',[
|
|||||||
|
|
||||||
Results.prototype.option = function (data) {
|
Results.prototype.option = function (data) {
|
||||||
var $option = $(
|
var $option = $(
|
||||||
'<li class="option" role="option" aria-selected="false"></li>'
|
'<li class="option" role="treeitem" aria-selected="false"></li>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.children) {
|
if (data.children) {
|
||||||
$option
|
$option
|
||||||
.addClass('group')
|
.attr('role', 'group')
|
||||||
|
.attr('aria-label', data.text)
|
||||||
.removeAttr('aria-selected');
|
.removeAttr('aria-selected');
|
||||||
|
|
||||||
var $label = $('<strong class="group-label"></strong>');
|
var $label = $('<strong class="group-label"></strong>');
|
||||||
@ -726,20 +727,32 @@ define('select2/results',[
|
|||||||
self.clear();
|
self.clear();
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('results:append', function (params) {
|
container.on('results:append', function (params) {
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('select', function () {
|
container.on('select', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('unselect', function () {
|
container.on('unselect', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -1009,13 +1022,14 @@ define('select2/selection/single',[
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('open', function () {
|
container.on('open', function () {
|
||||||
// When the dropdown is open, aria-expended="true"
|
// When the dropdown is open, aria-expanded="true"
|
||||||
self.$selection.attr('aria-expanded', 'true');
|
self.$selection.attr('aria-expanded', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('close', function () {
|
container.on('close', function () {
|
||||||
// When the dropdown is closed, aria-expended="false"
|
// When the dropdown is closed, aria-expanded="false"
|
||||||
self.$selection.attr('aria-expanded', 'false');
|
self.$selection.attr('aria-expanded', 'false');
|
||||||
|
self.$selection.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$selection.on('focus', function (evt) {
|
this.$selection.on('focus', function (evt) {
|
||||||
@ -1084,10 +1098,6 @@ define('select2/selection/single',[
|
|||||||
var formatted = this.display(selection);
|
var formatted = this.display(selection);
|
||||||
|
|
||||||
this.$selection.find('.rendered-selection').html(formatted);
|
this.$selection.find('.rendered-selection').html(formatted);
|
||||||
|
|
||||||
if (data[0]._resultId != null) {
|
|
||||||
this.$selection.attr('aria-activedescendant', data[0]._resultId);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return SingleSelection;
|
return SingleSelection;
|
||||||
|
2
dist/js/select2.min.js
vendored
2
dist/js/select2.min.js
vendored
File diff suppressed because one or more lines are too long
19
src/js/select2/results.js
vendored
19
src/js/select2/results.js
vendored
@ -13,7 +13,7 @@ define([
|
|||||||
|
|
||||||
Results.prototype.render = function () {
|
Results.prototype.render = function () {
|
||||||
var $results = $(
|
var $results = $(
|
||||||
'<ul class="options" role="listbox"></ul>'
|
'<ul class="options" role="tree"></ul>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.options.get('multiple')) {
|
if (this.options.get('multiple')) {
|
||||||
@ -86,12 +86,13 @@ define([
|
|||||||
|
|
||||||
Results.prototype.option = function (data) {
|
Results.prototype.option = function (data) {
|
||||||
var $option = $(
|
var $option = $(
|
||||||
'<li class="option" role="option" aria-selected="false"></li>'
|
'<li class="option" role="treeitem" aria-selected="false"></li>'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (data.children) {
|
if (data.children) {
|
||||||
$option
|
$option
|
||||||
.addClass('group')
|
.attr('role', 'group')
|
||||||
|
.attr('aria-label', data.text)
|
||||||
.removeAttr('aria-selected');
|
.removeAttr('aria-selected');
|
||||||
|
|
||||||
var $label = $('<strong class="group-label"></strong>');
|
var $label = $('<strong class="group-label"></strong>');
|
||||||
@ -147,20 +148,32 @@ define([
|
|||||||
self.clear();
|
self.clear();
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('results:append', function (params) {
|
container.on('results:append', function (params) {
|
||||||
self.append(params.data);
|
self.append(params.data);
|
||||||
|
|
||||||
|
if (container.isOpen()) {
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('select', function () {
|
container.on('select', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('unselect', function () {
|
container.on('unselect', function () {
|
||||||
|
if (!container.isOpen()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
self.setClasses();
|
self.setClasses();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
9
src/js/select2/selection/single.js
vendored
9
src/js/select2/selection/single.js
vendored
@ -48,13 +48,14 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('open', function () {
|
container.on('open', function () {
|
||||||
// When the dropdown is open, aria-expended="true"
|
// When the dropdown is open, aria-expanded="true"
|
||||||
self.$selection.attr('aria-expanded', 'true');
|
self.$selection.attr('aria-expanded', 'true');
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('close', function () {
|
container.on('close', function () {
|
||||||
// When the dropdown is closed, aria-expended="false"
|
// When the dropdown is closed, aria-expanded="false"
|
||||||
self.$selection.attr('aria-expanded', 'false');
|
self.$selection.attr('aria-expanded', 'false');
|
||||||
|
self.$selection.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$selection.on('focus', function (evt) {
|
this.$selection.on('focus', function (evt) {
|
||||||
@ -123,10 +124,6 @@ define([
|
|||||||
var formatted = this.display(selection);
|
var formatted = this.display(selection);
|
||||||
|
|
||||||
this.$selection.find('.rendered-selection').html(formatted);
|
this.$selection.find('.rendered-selection').html(formatted);
|
||||||
|
|
||||||
if (data[0]._resultId != null) {
|
|
||||||
this.$selection.attr('aria-activedescendant', data[0]._resultId);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return SingleSelection;
|
return SingleSelection;
|
||||||
|
@ -85,7 +85,7 @@
|
|||||||
|
|
||||||
.options {
|
.options {
|
||||||
.option {
|
.option {
|
||||||
&.group {
|
&[role=group] {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
.group-label {
|
.group-label {
|
||||||
|
Loading…
Reference in New Issue
Block a user