1
0
mirror of synced 2025-02-16 20:13:16 +03:00

Better accessibility for results

Options are correctly called out as focus moves around the results
list.
This commit is contained in:
Kevin Brown 2014-10-19 19:49:06 -04:00
parent 00a31df47a
commit 7c8601d33b
12 changed files with 221 additions and 26 deletions

View File

@ -117,7 +117,7 @@
display: block;
padding: 6px; }
.select2-container.select2-theme-default .dropdown .results .options .option[aria-disabled=true] {
color: #666; }
color: #999; }
.select2-container.select2-theme-default .dropdown .results .options .option[aria-selected=true] {
background-color: #ddd; }
.select2-container.select2-theme-default .dropdown .results .options .option[aria-selected].highlighted {

File diff suppressed because one or more lines are too long

View File

@ -154,6 +154,7 @@ define('select2/results',[
function Results ($element, options, dataAdapter) {
this.$element = $element;
this.data = dataAdapter;
this.options = options;
Results.__super__.constructor.call(this);
}
@ -165,6 +166,10 @@ define('select2/results',[
'<ul class="options" role="listbox"></ul>'
);
if (this.options.get('multiple')) {
$results.attr('aria-multiselectable', 'true');
}
this.$results = $results;
return $results;
@ -284,6 +289,10 @@ define('select2/results',[
Results.prototype.bind = function (container, $container) {
var self = this;
var id = container.id + '-results';
this.$results.attr('id', id);
container.on('results:all', function (params) {
self.clear();
self.append(params.data);
@ -308,6 +317,7 @@ define('select2/results',[
container.on('open', function () {
// When the dropdown is open, aria-expended="true"
self.$results.attr('aria-expanded', 'true');
self.$results.attr('aria-hidden', 'false');
self.setClasses();
});
@ -315,6 +325,8 @@ define('select2/results',[
container.on('close', function () {
// When the dropdown is closed, aria-expended="false"
self.$results.attr('aria-expanded', 'false');
self.$results.attr('aria-hidden', 'true');
self.$results.removeAttr('aria-activedescendant');
});
container.on('results:select', function () {
@ -401,6 +413,10 @@ define('select2/results',[
}
});
container.on('results:focus', function (params) {
params.element.addClass('highlighted');
});
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
var $this = $(this);
@ -422,12 +438,21 @@ define('select2/results',[
});
this.$results.on('mouseenter', '.option[aria-selected]', function (evt) {
var data = $(this).data('data');
self.$results.find('.option.highlighted').removeClass('highlighted');
$(this).addClass('highlighted');
self.trigger('results:focus', {
data: data,
element: $(this)
});
});
this.$results.on('mouseleave', '.option', function (evt) {
$(this).removeClass('highlighted');
if ($(this).hasClass('highlighted')) {
$(this).removeClass('highlighted');
self.$results.removeAttr('aria-activedescendant');
}
});
};
@ -537,9 +562,11 @@ define('select2/selection/single',[
SingleSelection.__super__.bind.apply(this, arguments);
var id = container.id + '-container';
var resultsId = container.id + '-results';
this.$selection.find('.rendered-selection').attr('id', id);
this.$selection.attr('aria-labelledby', id);
this.$selection.attr('aria-owns', resultsId);
this.$selection.on('mousedown', function (evt) {
// Only respond to left clicks
@ -576,6 +603,8 @@ define('select2/selection/single',[
if (container.isOpen()) {
if (key == KEYS.ENTER) {
self.trigger('results:select');
evt.preventDefault();
} else if (key == KEYS.UP) {
self.trigger('results:previous');
@ -588,10 +617,16 @@ define('select2/selection/single',[
} else {
if (key == KEYS.ENTER || key == KEYS.SPACE) {
self.trigger('open');
evt.preventDefault();
}
}
});
container.on('results:focus', function (params) {
self.$selection.attr('aria-activedescendant', params.data._resultId);
});
container.on('selection:update', function (params) {
self.update(params.data);
});
@ -622,7 +657,7 @@ define('select2/selection/single',[
this.$selection.find('.rendered-selection').html(formatted);
if (data[0]._resultId != null) {
this.$selection.attr('aria-activedescendent', data[0]._resultId);
this.$selection.attr('aria-activedescendant', data[0]._resultId);
}
};
@ -1404,6 +1439,10 @@ define('select2/core',[
self.trigger('close');
});
this.results.on('results:focus', function (params) {
self.trigger('results:focus', params);
});
this.on('open', function () {
$container.addClass('open');
});
@ -1455,7 +1494,7 @@ define('select2/core',[
var $container = $(
'<span class="select2 select2-container select2-theme-default">' +
'<span class="selection"></span>' +
'<span class="dropdown-wrapper"></span>' +
'<span class="dropdown-wrapper" aria-hidden="true"></span>' +
'</span>'
);

View File

@ -154,6 +154,7 @@ define('select2/results',[
function Results ($element, options, dataAdapter) {
this.$element = $element;
this.data = dataAdapter;
this.options = options;
Results.__super__.constructor.call(this);
}
@ -165,6 +166,10 @@ define('select2/results',[
'<ul class="options" role="listbox"></ul>'
);
if (this.options.get('multiple')) {
$results.attr('aria-multiselectable', 'true');
}
this.$results = $results;
return $results;
@ -284,6 +289,10 @@ define('select2/results',[
Results.prototype.bind = function (container, $container) {
var self = this;
var id = container.id + '-results';
this.$results.attr('id', id);
container.on('results:all', function (params) {
self.clear();
self.append(params.data);
@ -308,6 +317,7 @@ define('select2/results',[
container.on('open', function () {
// When the dropdown is open, aria-expended="true"
self.$results.attr('aria-expanded', 'true');
self.$results.attr('aria-hidden', 'false');
self.setClasses();
});
@ -315,6 +325,8 @@ define('select2/results',[
container.on('close', function () {
// When the dropdown is closed, aria-expended="false"
self.$results.attr('aria-expanded', 'false');
self.$results.attr('aria-hidden', 'true');
self.$results.removeAttr('aria-activedescendant');
});
container.on('results:select', function () {
@ -401,6 +413,10 @@ define('select2/results',[
}
});
container.on('results:focus', function (params) {
params.element.addClass('highlighted');
});
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
var $this = $(this);
@ -422,12 +438,21 @@ define('select2/results',[
});
this.$results.on('mouseenter', '.option[aria-selected]', function (evt) {
var data = $(this).data('data');
self.$results.find('.option.highlighted').removeClass('highlighted');
$(this).addClass('highlighted');
self.trigger('results:focus', {
data: data,
element: $(this)
});
});
this.$results.on('mouseleave', '.option', function (evt) {
$(this).removeClass('highlighted');
if ($(this).hasClass('highlighted')) {
$(this).removeClass('highlighted');
self.$results.removeAttr('aria-activedescendant');
}
});
};
@ -537,9 +562,11 @@ define('select2/selection/single',[
SingleSelection.__super__.bind.apply(this, arguments);
var id = container.id + '-container';
var resultsId = container.id + '-results';
this.$selection.find('.rendered-selection').attr('id', id);
this.$selection.attr('aria-labelledby', id);
this.$selection.attr('aria-owns', resultsId);
this.$selection.on('mousedown', function (evt) {
// Only respond to left clicks
@ -576,6 +603,8 @@ define('select2/selection/single',[
if (container.isOpen()) {
if (key == KEYS.ENTER) {
self.trigger('results:select');
evt.preventDefault();
} else if (key == KEYS.UP) {
self.trigger('results:previous');
@ -588,10 +617,16 @@ define('select2/selection/single',[
} else {
if (key == KEYS.ENTER || key == KEYS.SPACE) {
self.trigger('open');
evt.preventDefault();
}
}
});
container.on('results:focus', function (params) {
self.$selection.attr('aria-activedescendant', params.data._resultId);
});
container.on('selection:update', function (params) {
self.update(params.data);
});
@ -622,7 +657,7 @@ define('select2/selection/single',[
this.$selection.find('.rendered-selection').html(formatted);
if (data[0]._resultId != null) {
this.$selection.attr('aria-activedescendent', data[0]._resultId);
this.$selection.attr('aria-activedescendant', data[0]._resultId);
}
};
@ -1404,6 +1439,10 @@ define('select2/core',[
self.trigger('close');
});
this.results.on('results:focus', function (params) {
self.trigger('results:focus', params);
});
this.on('open', function () {
$container.addClass('open');
});
@ -1455,7 +1494,7 @@ define('select2/core',[
var $container = $(
'<span class="select2 select2-container select2-theme-default">' +
'<span class="selection"></span>' +
'<span class="dropdown-wrapper"></span>' +
'<span class="dropdown-wrapper" aria-hidden="true"></span>' +
'</span>'
);

View File

@ -9692,6 +9692,7 @@ define('select2/results',[
function Results ($element, options, dataAdapter) {
this.$element = $element;
this.data = dataAdapter;
this.options = options;
Results.__super__.constructor.call(this);
}
@ -9703,6 +9704,10 @@ define('select2/results',[
'<ul class="options" role="listbox"></ul>'
);
if (this.options.get('multiple')) {
$results.attr('aria-multiselectable', 'true');
}
this.$results = $results;
return $results;
@ -9822,6 +9827,10 @@ define('select2/results',[
Results.prototype.bind = function (container, $container) {
var self = this;
var id = container.id + '-results';
this.$results.attr('id', id);
container.on('results:all', function (params) {
self.clear();
self.append(params.data);
@ -9846,6 +9855,7 @@ define('select2/results',[
container.on('open', function () {
// When the dropdown is open, aria-expended="true"
self.$results.attr('aria-expanded', 'true');
self.$results.attr('aria-hidden', 'false');
self.setClasses();
});
@ -9853,6 +9863,8 @@ define('select2/results',[
container.on('close', function () {
// When the dropdown is closed, aria-expended="false"
self.$results.attr('aria-expanded', 'false');
self.$results.attr('aria-hidden', 'true');
self.$results.removeAttr('aria-activedescendant');
});
container.on('results:select', function () {
@ -9939,6 +9951,10 @@ define('select2/results',[
}
});
container.on('results:focus', function (params) {
params.element.addClass('highlighted');
});
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
var $this = $(this);
@ -9960,12 +9976,21 @@ define('select2/results',[
});
this.$results.on('mouseenter', '.option[aria-selected]', function (evt) {
var data = $(this).data('data');
self.$results.find('.option.highlighted').removeClass('highlighted');
$(this).addClass('highlighted');
self.trigger('results:focus', {
data: data,
element: $(this)
});
});
this.$results.on('mouseleave', '.option', function (evt) {
$(this).removeClass('highlighted');
if ($(this).hasClass('highlighted')) {
$(this).removeClass('highlighted');
self.$results.removeAttr('aria-activedescendant');
}
});
};
@ -10075,9 +10100,11 @@ define('select2/selection/single',[
SingleSelection.__super__.bind.apply(this, arguments);
var id = container.id + '-container';
var resultsId = container.id + '-results';
this.$selection.find('.rendered-selection').attr('id', id);
this.$selection.attr('aria-labelledby', id);
this.$selection.attr('aria-owns', resultsId);
this.$selection.on('mousedown', function (evt) {
// Only respond to left clicks
@ -10114,6 +10141,8 @@ define('select2/selection/single',[
if (container.isOpen()) {
if (key == KEYS.ENTER) {
self.trigger('results:select');
evt.preventDefault();
} else if (key == KEYS.UP) {
self.trigger('results:previous');
@ -10126,10 +10155,16 @@ define('select2/selection/single',[
} else {
if (key == KEYS.ENTER || key == KEYS.SPACE) {
self.trigger('open');
evt.preventDefault();
}
}
});
container.on('results:focus', function (params) {
self.$selection.attr('aria-activedescendant', params.data._resultId);
});
container.on('selection:update', function (params) {
self.update(params.data);
});
@ -10160,7 +10195,7 @@ define('select2/selection/single',[
this.$selection.find('.rendered-selection').html(formatted);
if (data[0]._resultId != null) {
this.$selection.attr('aria-activedescendent', data[0]._resultId);
this.$selection.attr('aria-activedescendant', data[0]._resultId);
}
};
@ -10942,6 +10977,10 @@ define('select2/core',[
self.trigger('close');
});
this.results.on('results:focus', function (params) {
self.trigger('results:focus', params);
});
this.on('open', function () {
$container.addClass('open');
});
@ -10993,7 +11032,7 @@ define('select2/core',[
var $container = $(
'<span class="select2 select2-container select2-theme-default">' +
'<span class="selection"></span>' +
'<span class="dropdown-wrapper"></span>' +
'<span class="dropdown-wrapper" aria-hidden="true"></span>' +
'</span>'
);

File diff suppressed because one or more lines are too long

47
dist/js/select2.js vendored
View File

@ -583,6 +583,7 @@ define('select2/results',[
function Results ($element, options, dataAdapter) {
this.$element = $element;
this.data = dataAdapter;
this.options = options;
Results.__super__.constructor.call(this);
}
@ -594,6 +595,10 @@ define('select2/results',[
'<ul class="options" role="listbox"></ul>'
);
if (this.options.get('multiple')) {
$results.attr('aria-multiselectable', 'true');
}
this.$results = $results;
return $results;
@ -713,6 +718,10 @@ define('select2/results',[
Results.prototype.bind = function (container, $container) {
var self = this;
var id = container.id + '-results';
this.$results.attr('id', id);
container.on('results:all', function (params) {
self.clear();
self.append(params.data);
@ -737,6 +746,7 @@ define('select2/results',[
container.on('open', function () {
// When the dropdown is open, aria-expended="true"
self.$results.attr('aria-expanded', 'true');
self.$results.attr('aria-hidden', 'false');
self.setClasses();
});
@ -744,6 +754,8 @@ define('select2/results',[
container.on('close', function () {
// When the dropdown is closed, aria-expended="false"
self.$results.attr('aria-expanded', 'false');
self.$results.attr('aria-hidden', 'true');
self.$results.removeAttr('aria-activedescendant');
});
container.on('results:select', function () {
@ -830,6 +842,10 @@ define('select2/results',[
}
});
container.on('results:focus', function (params) {
params.element.addClass('highlighted');
});
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
var $this = $(this);
@ -851,12 +867,21 @@ define('select2/results',[
});
this.$results.on('mouseenter', '.option[aria-selected]', function (evt) {
var data = $(this).data('data');
self.$results.find('.option.highlighted').removeClass('highlighted');
$(this).addClass('highlighted');
self.trigger('results:focus', {
data: data,
element: $(this)
});
});
this.$results.on('mouseleave', '.option', function (evt) {
$(this).removeClass('highlighted');
if ($(this).hasClass('highlighted')) {
$(this).removeClass('highlighted');
self.$results.removeAttr('aria-activedescendant');
}
});
};
@ -966,9 +991,11 @@ define('select2/selection/single',[
SingleSelection.__super__.bind.apply(this, arguments);
var id = container.id + '-container';
var resultsId = container.id + '-results';
this.$selection.find('.rendered-selection').attr('id', id);
this.$selection.attr('aria-labelledby', id);
this.$selection.attr('aria-owns', resultsId);
this.$selection.on('mousedown', function (evt) {
// Only respond to left clicks
@ -1005,6 +1032,8 @@ define('select2/selection/single',[
if (container.isOpen()) {
if (key == KEYS.ENTER) {
self.trigger('results:select');
evt.preventDefault();
} else if (key == KEYS.UP) {
self.trigger('results:previous');
@ -1017,10 +1046,16 @@ define('select2/selection/single',[
} else {
if (key == KEYS.ENTER || key == KEYS.SPACE) {
self.trigger('open');
evt.preventDefault();
}
}
});
container.on('results:focus', function (params) {
self.$selection.attr('aria-activedescendant', params.data._resultId);
});
container.on('selection:update', function (params) {
self.update(params.data);
});
@ -1051,7 +1086,7 @@ define('select2/selection/single',[
this.$selection.find('.rendered-selection').html(formatted);
if (data[0]._resultId != null) {
this.$selection.attr('aria-activedescendent', data[0]._resultId);
this.$selection.attr('aria-activedescendant', data[0]._resultId);
}
};
@ -1833,6 +1868,10 @@ define('select2/core',[
self.trigger('close');
});
this.results.on('results:focus', function (params) {
self.trigger('results:focus', params);
});
this.on('open', function () {
$container.addClass('open');
});
@ -1884,7 +1923,7 @@ define('select2/core',[
var $container = $(
'<span class="select2 select2-container select2-theme-default">' +
'<span class="selection"></span>' +
'<span class="dropdown-wrapper"></span>' +
'<span class="dropdown-wrapper" aria-hidden="true"></span>' +
'</span>'
);

File diff suppressed because one or more lines are too long

View File

@ -116,6 +116,10 @@ define([
self.trigger('close');
});
this.results.on('results:focus', function (params) {
self.trigger('results:focus', params);
});
this.on('open', function () {
$container.addClass('open');
});
@ -167,7 +171,7 @@ define([
var $container = $(
'<span class="select2 select2-container select2-theme-default">' +
'<span class="selection"></span>' +
'<span class="dropdown-wrapper"></span>' +
'<span class="dropdown-wrapper" aria-hidden="true"></span>' +
'</span>'
);

View File

@ -4,6 +4,7 @@ define([
function Results ($element, options, dataAdapter) {
this.$element = $element;
this.data = dataAdapter;
this.options = options;
Results.__super__.constructor.call(this);
}
@ -15,6 +16,10 @@ define([
'<ul class="options" role="listbox"></ul>'
);
if (this.options.get('multiple')) {
$results.attr('aria-multiselectable', 'true');
}
this.$results = $results;
return $results;
@ -134,6 +139,10 @@ define([
Results.prototype.bind = function (container, $container) {
var self = this;
var id = container.id + '-results';
this.$results.attr('id', id);
container.on('results:all', function (params) {
self.clear();
self.append(params.data);
@ -158,6 +167,7 @@ define([
container.on('open', function () {
// When the dropdown is open, aria-expended="true"
self.$results.attr('aria-expanded', 'true');
self.$results.attr('aria-hidden', 'false');
self.setClasses();
});
@ -165,6 +175,8 @@ define([
container.on('close', function () {
// When the dropdown is closed, aria-expended="false"
self.$results.attr('aria-expanded', 'false');
self.$results.attr('aria-hidden', 'true');
self.$results.removeAttr('aria-activedescendant');
});
container.on('results:select', function () {
@ -251,6 +263,10 @@ define([
}
});
container.on('results:focus', function (params) {
params.element.addClass('highlighted');
});
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
var $this = $(this);
@ -272,12 +288,21 @@ define([
});
this.$results.on('mouseenter', '.option[aria-selected]', function (evt) {
var data = $(this).data('data');
self.$results.find('.option.highlighted').removeClass('highlighted');
$(this).addClass('highlighted');
self.trigger('results:focus', {
data: data,
element: $(this)
});
});
this.$results.on('mouseleave', '.option', function (evt) {
$(this).removeClass('highlighted');
if ($(this).hasClass('highlighted')) {
$(this).removeClass('highlighted');
self.$results.removeAttr('aria-activedescendant');
}
});
};

View File

@ -30,9 +30,11 @@ define([
SingleSelection.__super__.bind.apply(this, arguments);
var id = container.id + '-container';
var resultsId = container.id + '-results';
this.$selection.find('.rendered-selection').attr('id', id);
this.$selection.attr('aria-labelledby', id);
this.$selection.attr('aria-owns', resultsId);
this.$selection.on('mousedown', function (evt) {
// Only respond to left clicks
@ -69,6 +71,8 @@ define([
if (container.isOpen()) {
if (key == KEYS.ENTER) {
self.trigger('results:select');
evt.preventDefault();
} else if (key == KEYS.UP) {
self.trigger('results:previous');
@ -81,10 +85,16 @@ define([
} else {
if (key == KEYS.ENTER || key == KEYS.SPACE) {
self.trigger('open');
evt.preventDefault();
}
}
});
container.on('results:focus', function (params) {
self.$selection.attr('aria-activedescendant', params.data._resultId);
});
container.on('selection:update', function (params) {
self.update(params.data);
});
@ -115,7 +125,7 @@ define([
this.$selection.find('.rendered-selection').html(formatted);
if (data[0]._resultId != null) {
this.$selection.attr('aria-activedescendent', data[0]._resultId);
this.$selection.attr('aria-activedescendant', data[0]._resultId);
}
};

View File

@ -96,7 +96,7 @@
}
&[aria-disabled=true] {
color: #666;
color: #999;
}
&[aria-selected=true] {