Skip to content

Commit 7c8601d

Browse files
committed
Better accessibility for results
Options are correctly called out as focus moves around the results list.
1 parent 00a31df commit 7c8601d

12 files changed

Lines changed: 221 additions & 26 deletions

File tree

dist/css/select2.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@
117117
display: block;
118118
padding: 6px; }
119119
.select2-container.select2-theme-default .dropdown .results .options .option[aria-disabled=true] {
120-
color: #666; }
120+
color: #999; }
121121
.select2-container.select2-theme-default .dropdown .results .options .option[aria-selected=true] {
122122
background-color: #ddd; }
123123
.select2-container.select2-theme-default .dropdown .results .options .option[aria-selected].highlighted {

dist/css/select2.min.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/js/select2.amd.full.js

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ define('select2/results',[
154154
function Results ($element, options, dataAdapter) {
155155
this.$element = $element;
156156
this.data = dataAdapter;
157+
this.options = options;
157158

158159
Results.__super__.constructor.call(this);
159160
}
@@ -165,6 +166,10 @@ define('select2/results',[
165166
'<ul class="options" role="listbox"></ul>'
166167
);
167168

169+
if (this.options.get('multiple')) {
170+
$results.attr('aria-multiselectable', 'true');
171+
}
172+
168173
this.$results = $results;
169174

170175
return $results;
@@ -284,6 +289,10 @@ define('select2/results',[
284289
Results.prototype.bind = function (container, $container) {
285290
var self = this;
286291

292+
var id = container.id + '-results';
293+
294+
this.$results.attr('id', id);
295+
287296
container.on('results:all', function (params) {
288297
self.clear();
289298
self.append(params.data);
@@ -308,13 +317,16 @@ define('select2/results',[
308317
container.on('open', function () {
309318
// When the dropdown is open, aria-expended="true"
310319
self.$results.attr('aria-expanded', 'true');
320+
self.$results.attr('aria-hidden', 'false');
311321

312322
self.setClasses();
313323
});
314324

315325
container.on('close', function () {
316326
// When the dropdown is closed, aria-expended="false"
317327
self.$results.attr('aria-expanded', 'false');
328+
self.$results.attr('aria-hidden', 'true');
329+
self.$results.removeAttr('aria-activedescendant');
318330
});
319331

320332
container.on('results:select', function () {
@@ -401,6 +413,10 @@ define('select2/results',[
401413
}
402414
});
403415

416+
container.on('results:focus', function (params) {
417+
params.element.addClass('highlighted');
418+
});
419+
404420
this.$results.on('mouseup', '.option[aria-selected]', function (evt) {
405421
var $this = $(this);
406422

@@ -422,12 +438,21 @@ define('select2/results',[
422438
});
423439

424440
this.$results.on('mouseenter', '.option[aria-selected]', function (evt) {
441+
var data = $(this).data('data');
442+
425443
self.$results.find('.option.highlighted').removeClass('highlighted');
426-
$(this).addClass('highlighted');
444+
445+
self.trigger('results:focus', {
446+
data: data,
447+
element: $(this)
448+
});
427449
});
428450

429451
this.$results.on('mouseleave', '.option', function (evt) {
430-
$(this).removeClass('highlighted');
452+
if ($(this).hasClass('highlighted')) {
453+
$(this).removeClass('highlighted');
454+
self.$results.removeAttr('aria-activedescendant');
455+
}
431456
});
432457
};
433458

@@ -537,9 +562,11 @@ define('select2/selection/single',[
537562
SingleSelection.__super__.bind.apply(this, arguments);
538563

539564
var id = container.id + '-container';
565+
var resultsId = container.id + '-results';
540566

541567
this.$selection.find('.rendered-selection').attr('id', id);
542568
this.$selection.attr('aria-labelledby', id);
569+
this.$selection.attr('aria-owns', resultsId);
543570

544571
this.$selection.on('mousedown', function (evt) {
545572
// Only respond to left clicks
@@ -576,6 +603,8 @@ define('select2/selection/single',[
576603
if (container.isOpen()) {
577604
if (key == KEYS.ENTER) {
578605
self.trigger('results:select');
606+
607+
evt.preventDefault();
579608
} else if (key == KEYS.UP) {
580609
self.trigger('results:previous');
581610

@@ -588,10 +617,16 @@ define('select2/selection/single',[
588617
} else {
589618
if (key == KEYS.ENTER || key == KEYS.SPACE) {
590619
self.trigger('open');
620+
621+
evt.preventDefault();
591622
}
592623
}
593624
});
594625

626+
container.on('results:focus', function (params) {
627+
self.$selection.attr('aria-activedescendant', params.data._resultId);
628+
});
629+
595630
container.on('selection:update', function (params) {
596631
self.update(params.data);
597632
});
@@ -622,7 +657,7 @@ define('select2/selection/single',[
622657
this.$selection.find('.rendered-selection').html(formatted);
623658

624659
if (data[0]._resultId != null) {
625-
this.$selection.attr('aria-activedescendent', data[0]._resultId);
660+
this.$selection.attr('aria-activedescendant', data[0]._resultId);
626661
}
627662
};
628663

@@ -1404,6 +1439,10 @@ define('select2/core',[
14041439
self.trigger('close');
14051440
});
14061441

1442+
this.results.on('results:focus', function (params) {
1443+
self.trigger('results:focus', params);
1444+
});
1445+
14071446
this.on('open', function () {
14081447
$container.addClass('open');
14091448
});
@@ -1455,7 +1494,7 @@ define('select2/core',[
14551494
var $container = $(
14561495
'<span class="select2 select2-container select2-theme-default">' +
14571496
'<span class="selection"></span>' +
1458-
'<span class="dropdown-wrapper"></span>' +
1497+
'<span class="dropdown-wrapper" aria-hidden="true"></span>' +
14591498
'</span>'
14601499
);
14611500

0 commit comments

Comments
 (0)