Single select accessibility
Now the Select2 instance is correctly recognized as a combobox, and the selected option is read aloud when it is focused, just like in a standard select box. This works by generating semi-random ids that are used for the ARIA attributes. These are not intended to be consistent by any means, they are just generated to make the instance accessible by screen readers.
This commit is contained in:
parent
8dfd6d6960
commit
9ada3712f7
58
dist/js/select2.amd.full.js
vendored
58
dist/js/select2.amd.full.js
vendored
@ -258,7 +258,11 @@ define('select2/results',[
|
||||
}
|
||||
|
||||
if (data.id == null) {
|
||||
$option.removeClass('aria-selected');
|
||||
$option.removeAttr('aria-selected');
|
||||
}
|
||||
|
||||
if (data._resultId != null) {
|
||||
$option.attr('id', data._resultId);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
@ -363,6 +367,8 @@ define('select2/results',[
|
||||
var $next = $options.eq(nextIndex);
|
||||
|
||||
$next.trigger('mouseenter');
|
||||
console.log($next.offset().top, self.$results.parent().scrollTop());
|
||||
//self.$results.parents().scrollTop($next.offset().top);
|
||||
});
|
||||
|
||||
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
|
||||
@ -482,13 +488,24 @@ define('select2/selection/single',[
|
||||
|
||||
SingleSelection.prototype.render = function () {
|
||||
var $selection = $(
|
||||
'<span class="single-select" tabindex="0">' +
|
||||
'<span class="single-select" tabindex="0" role="combobox" ' +
|
||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||
'<span class="rendered-selection"></span>' +
|
||||
'</span>'
|
||||
);
|
||||
|
||||
$selection.attr('title', this.$element.attr('title'));
|
||||
|
||||
var id = 'select2-container-';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
$selection.find('.rendered-selection').attr('id', id);
|
||||
$selection.attr('aria-labelledby', id);
|
||||
|
||||
this.$selection = $selection;
|
||||
|
||||
return $selection;
|
||||
@ -510,6 +527,16 @@ define('select2/selection/single',[
|
||||
});
|
||||
});
|
||||
|
||||
container.on('open', function () {
|
||||
// When the dropdown is open, aria-expended="true"
|
||||
self.$selection.attr('aria-expanded', 'true');
|
||||
});
|
||||
|
||||
container.on('close', function () {
|
||||
// When the dropdown is closed, aria-expended="false"
|
||||
self.$selection.attr('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
this.$selection.on('focus', function (evt) {
|
||||
// User focuses on the container
|
||||
});
|
||||
@ -564,6 +591,10 @@ define('select2/selection/single',[
|
||||
var formatted = this.display(selection);
|
||||
|
||||
this.$selection.find('.rendered-selection').html(formatted);
|
||||
|
||||
if (data[0]._resultId != null) {
|
||||
this.$selection.attr('aria-activedescendent', data[0]._resultId);
|
||||
}
|
||||
};
|
||||
|
||||
return SingleSelection;
|
||||
@ -727,6 +758,25 @@ define('select2/data/base',[
|
||||
// Can be implemented in subclasses
|
||||
};
|
||||
|
||||
BaseAdapter.prototype.generateResultId = function (data) {
|
||||
var id = '';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
if (data.id != null) {
|
||||
id += '-' + data.id.toString();
|
||||
} else {
|
||||
for (var s = 0; s < 4; s++) {
|
||||
var idChar = Math.floor(Math.random() * 16);
|
||||
id += idChar.toString(16);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
return BaseAdapter;
|
||||
});
|
||||
|
||||
@ -878,6 +928,10 @@ define('select2/data/select',[
|
||||
data.children = children;
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
data._resultId = this.generateResultId(data);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
}
|
||||
|
||||
|
58
dist/js/select2.amd.js
vendored
58
dist/js/select2.amd.js
vendored
@ -258,7 +258,11 @@ define('select2/results',[
|
||||
}
|
||||
|
||||
if (data.id == null) {
|
||||
$option.removeClass('aria-selected');
|
||||
$option.removeAttr('aria-selected');
|
||||
}
|
||||
|
||||
if (data._resultId != null) {
|
||||
$option.attr('id', data._resultId);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
@ -363,6 +367,8 @@ define('select2/results',[
|
||||
var $next = $options.eq(nextIndex);
|
||||
|
||||
$next.trigger('mouseenter');
|
||||
console.log($next.offset().top, self.$results.parent().scrollTop());
|
||||
//self.$results.parents().scrollTop($next.offset().top);
|
||||
});
|
||||
|
||||
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
|
||||
@ -482,13 +488,24 @@ define('select2/selection/single',[
|
||||
|
||||
SingleSelection.prototype.render = function () {
|
||||
var $selection = $(
|
||||
'<span class="single-select" tabindex="0">' +
|
||||
'<span class="single-select" tabindex="0" role="combobox" ' +
|
||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||
'<span class="rendered-selection"></span>' +
|
||||
'</span>'
|
||||
);
|
||||
|
||||
$selection.attr('title', this.$element.attr('title'));
|
||||
|
||||
var id = 'select2-container-';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
$selection.find('.rendered-selection').attr('id', id);
|
||||
$selection.attr('aria-labelledby', id);
|
||||
|
||||
this.$selection = $selection;
|
||||
|
||||
return $selection;
|
||||
@ -510,6 +527,16 @@ define('select2/selection/single',[
|
||||
});
|
||||
});
|
||||
|
||||
container.on('open', function () {
|
||||
// When the dropdown is open, aria-expended="true"
|
||||
self.$selection.attr('aria-expanded', 'true');
|
||||
});
|
||||
|
||||
container.on('close', function () {
|
||||
// When the dropdown is closed, aria-expended="false"
|
||||
self.$selection.attr('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
this.$selection.on('focus', function (evt) {
|
||||
// User focuses on the container
|
||||
});
|
||||
@ -564,6 +591,10 @@ define('select2/selection/single',[
|
||||
var formatted = this.display(selection);
|
||||
|
||||
this.$selection.find('.rendered-selection').html(formatted);
|
||||
|
||||
if (data[0]._resultId != null) {
|
||||
this.$selection.attr('aria-activedescendent', data[0]._resultId);
|
||||
}
|
||||
};
|
||||
|
||||
return SingleSelection;
|
||||
@ -727,6 +758,25 @@ define('select2/data/base',[
|
||||
// Can be implemented in subclasses
|
||||
};
|
||||
|
||||
BaseAdapter.prototype.generateResultId = function (data) {
|
||||
var id = '';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
if (data.id != null) {
|
||||
id += '-' + data.id.toString();
|
||||
} else {
|
||||
for (var s = 0; s < 4; s++) {
|
||||
var idChar = Math.floor(Math.random() * 16);
|
||||
id += idChar.toString(16);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
return BaseAdapter;
|
||||
});
|
||||
|
||||
@ -878,6 +928,10 @@ define('select2/data/select',[
|
||||
data.children = children;
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
data._resultId = this.generateResultId(data);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
}
|
||||
|
||||
|
58
dist/js/select2.full.js
vendored
58
dist/js/select2.full.js
vendored
@ -9796,7 +9796,11 @@ define('select2/results',[
|
||||
}
|
||||
|
||||
if (data.id == null) {
|
||||
$option.removeClass('aria-selected');
|
||||
$option.removeAttr('aria-selected');
|
||||
}
|
||||
|
||||
if (data._resultId != null) {
|
||||
$option.attr('id', data._resultId);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
@ -9901,6 +9905,8 @@ define('select2/results',[
|
||||
var $next = $options.eq(nextIndex);
|
||||
|
||||
$next.trigger('mouseenter');
|
||||
console.log($next.offset().top, self.$results.parent().scrollTop());
|
||||
//self.$results.parents().scrollTop($next.offset().top);
|
||||
});
|
||||
|
||||
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
|
||||
@ -10020,13 +10026,24 @@ define('select2/selection/single',[
|
||||
|
||||
SingleSelection.prototype.render = function () {
|
||||
var $selection = $(
|
||||
'<span class="single-select" tabindex="0">' +
|
||||
'<span class="single-select" tabindex="0" role="combobox" ' +
|
||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||
'<span class="rendered-selection"></span>' +
|
||||
'</span>'
|
||||
);
|
||||
|
||||
$selection.attr('title', this.$element.attr('title'));
|
||||
|
||||
var id = 'select2-container-';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
$selection.find('.rendered-selection').attr('id', id);
|
||||
$selection.attr('aria-labelledby', id);
|
||||
|
||||
this.$selection = $selection;
|
||||
|
||||
return $selection;
|
||||
@ -10048,6 +10065,16 @@ define('select2/selection/single',[
|
||||
});
|
||||
});
|
||||
|
||||
container.on('open', function () {
|
||||
// When the dropdown is open, aria-expended="true"
|
||||
self.$selection.attr('aria-expanded', 'true');
|
||||
});
|
||||
|
||||
container.on('close', function () {
|
||||
// When the dropdown is closed, aria-expended="false"
|
||||
self.$selection.attr('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
this.$selection.on('focus', function (evt) {
|
||||
// User focuses on the container
|
||||
});
|
||||
@ -10102,6 +10129,10 @@ define('select2/selection/single',[
|
||||
var formatted = this.display(selection);
|
||||
|
||||
this.$selection.find('.rendered-selection').html(formatted);
|
||||
|
||||
if (data[0]._resultId != null) {
|
||||
this.$selection.attr('aria-activedescendent', data[0]._resultId);
|
||||
}
|
||||
};
|
||||
|
||||
return SingleSelection;
|
||||
@ -10265,6 +10296,25 @@ define('select2/data/base',[
|
||||
// Can be implemented in subclasses
|
||||
};
|
||||
|
||||
BaseAdapter.prototype.generateResultId = function (data) {
|
||||
var id = '';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
if (data.id != null) {
|
||||
id += '-' + data.id.toString();
|
||||
} else {
|
||||
for (var s = 0; s < 4; s++) {
|
||||
var idChar = Math.floor(Math.random() * 16);
|
||||
id += idChar.toString(16);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
return BaseAdapter;
|
||||
});
|
||||
|
||||
@ -10416,6 +10466,10 @@ define('select2/data/select',[
|
||||
data.children = children;
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
data._resultId = this.generateResultId(data);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
}
|
||||
|
||||
|
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
58
dist/js/select2.js
vendored
58
dist/js/select2.js
vendored
@ -687,7 +687,11 @@ define('select2/results',[
|
||||
}
|
||||
|
||||
if (data.id == null) {
|
||||
$option.removeClass('aria-selected');
|
||||
$option.removeAttr('aria-selected');
|
||||
}
|
||||
|
||||
if (data._resultId != null) {
|
||||
$option.attr('id', data._resultId);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
@ -792,6 +796,8 @@ define('select2/results',[
|
||||
var $next = $options.eq(nextIndex);
|
||||
|
||||
$next.trigger('mouseenter');
|
||||
console.log($next.offset().top, self.$results.parent().scrollTop());
|
||||
//self.$results.parents().scrollTop($next.offset().top);
|
||||
});
|
||||
|
||||
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
|
||||
@ -911,13 +917,24 @@ define('select2/selection/single',[
|
||||
|
||||
SingleSelection.prototype.render = function () {
|
||||
var $selection = $(
|
||||
'<span class="single-select" tabindex="0">' +
|
||||
'<span class="single-select" tabindex="0" role="combobox" ' +
|
||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||
'<span class="rendered-selection"></span>' +
|
||||
'</span>'
|
||||
);
|
||||
|
||||
$selection.attr('title', this.$element.attr('title'));
|
||||
|
||||
var id = 'select2-container-';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
$selection.find('.rendered-selection').attr('id', id);
|
||||
$selection.attr('aria-labelledby', id);
|
||||
|
||||
this.$selection = $selection;
|
||||
|
||||
return $selection;
|
||||
@ -939,6 +956,16 @@ define('select2/selection/single',[
|
||||
});
|
||||
});
|
||||
|
||||
container.on('open', function () {
|
||||
// When the dropdown is open, aria-expended="true"
|
||||
self.$selection.attr('aria-expanded', 'true');
|
||||
});
|
||||
|
||||
container.on('close', function () {
|
||||
// When the dropdown is closed, aria-expended="false"
|
||||
self.$selection.attr('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
this.$selection.on('focus', function (evt) {
|
||||
// User focuses on the container
|
||||
});
|
||||
@ -993,6 +1020,10 @@ define('select2/selection/single',[
|
||||
var formatted = this.display(selection);
|
||||
|
||||
this.$selection.find('.rendered-selection').html(formatted);
|
||||
|
||||
if (data[0]._resultId != null) {
|
||||
this.$selection.attr('aria-activedescendent', data[0]._resultId);
|
||||
}
|
||||
};
|
||||
|
||||
return SingleSelection;
|
||||
@ -1156,6 +1187,25 @@ define('select2/data/base',[
|
||||
// Can be implemented in subclasses
|
||||
};
|
||||
|
||||
BaseAdapter.prototype.generateResultId = function (data) {
|
||||
var id = '';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
if (data.id != null) {
|
||||
id += '-' + data.id.toString();
|
||||
} else {
|
||||
for (var s = 0; s < 4; s++) {
|
||||
var idChar = Math.floor(Math.random() * 16);
|
||||
id += idChar.toString(16);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
return BaseAdapter;
|
||||
});
|
||||
|
||||
@ -1307,6 +1357,10 @@ define('select2/data/select',[
|
||||
data.children = children;
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
data._resultId = this.generateResultId(data);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
}
|
||||
|
||||
|
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/data/base.js
vendored
19
src/js/select2/data/base.js
vendored
@ -19,5 +19,24 @@ define([
|
||||
// Can be implemented in subclasses
|
||||
};
|
||||
|
||||
BaseAdapter.prototype.generateResultId = function (data) {
|
||||
var id = '';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
if (data.id != null) {
|
||||
id += '-' + data.id.toString();
|
||||
} else {
|
||||
for (var s = 0; s < 4; s++) {
|
||||
var idChar = Math.floor(Math.random() * 16);
|
||||
id += idChar.toString(16);
|
||||
}
|
||||
}
|
||||
return id;
|
||||
};
|
||||
|
||||
return BaseAdapter;
|
||||
});
|
||||
|
4
src/js/select2/data/select.js
vendored
4
src/js/select2/data/select.js
vendored
@ -146,6 +146,10 @@ define([
|
||||
data.children = children;
|
||||
}
|
||||
|
||||
if (data.id) {
|
||||
data._resultId = this.generateResultId(data);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
}
|
||||
|
||||
|
8
src/js/select2/results.js
vendored
8
src/js/select2/results.js
vendored
@ -119,7 +119,11 @@ define([
|
||||
}
|
||||
|
||||
if (data.id == null) {
|
||||
$option.removeClass('aria-selected');
|
||||
$option.removeAttr('aria-selected');
|
||||
}
|
||||
|
||||
if (data._resultId != null) {
|
||||
$option.attr('id', data._resultId);
|
||||
}
|
||||
|
||||
$option.data('data', data);
|
||||
@ -224,6 +228,8 @@ define([
|
||||
var $next = $options.eq(nextIndex);
|
||||
|
||||
$next.trigger('mouseenter');
|
||||
console.log($next.offset().top, self.$results.parent().scrollTop());
|
||||
//self.$results.parents().scrollTop($next.offset().top);
|
||||
});
|
||||
|
||||
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
|
||||
|
27
src/js/select2/selection/single.js
vendored
27
src/js/select2/selection/single.js
vendored
@ -11,13 +11,24 @@ define([
|
||||
|
||||
SingleSelection.prototype.render = function () {
|
||||
var $selection = $(
|
||||
'<span class="single-select" tabindex="0">' +
|
||||
'<span class="single-select" tabindex="0" role="combobox" ' +
|
||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||
'<span class="rendered-selection"></span>' +
|
||||
'</span>'
|
||||
);
|
||||
|
||||
$selection.attr('title', this.$element.attr('title'));
|
||||
|
||||
var id = 'select2-container-';
|
||||
|
||||
for (var i = 0; i < 4; i++) {
|
||||
var r = Math.floor(Math.random() * 16);
|
||||
id += r.toString(16);
|
||||
}
|
||||
|
||||
$selection.find('.rendered-selection').attr('id', id);
|
||||
$selection.attr('aria-labelledby', id);
|
||||
|
||||
this.$selection = $selection;
|
||||
|
||||
return $selection;
|
||||
@ -39,6 +50,16 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
container.on('open', function () {
|
||||
// When the dropdown is open, aria-expended="true"
|
||||
self.$selection.attr('aria-expanded', 'true');
|
||||
});
|
||||
|
||||
container.on('close', function () {
|
||||
// When the dropdown is closed, aria-expended="false"
|
||||
self.$selection.attr('aria-expanded', 'false');
|
||||
});
|
||||
|
||||
this.$selection.on('focus', function (evt) {
|
||||
// User focuses on the container
|
||||
});
|
||||
@ -93,6 +114,10 @@ define([
|
||||
var formatted = this.display(selection);
|
||||
|
||||
this.$selection.find('.rendered-selection').html(formatted);
|
||||
|
||||
if (data[0]._resultId != null) {
|
||||
this.$selection.attr('aria-activedescendent', data[0]._resultId);
|
||||
}
|
||||
};
|
||||
|
||||
return SingleSelection;
|
||||
|
Loading…
Reference in New Issue
Block a user