Clone the tabindex
from the original select
Previously Select2 would assume that the tab index for the `<select>` was `0`, which is the browser default. Now Select2 will clone the tab index from the original element, and correctly restore it when it is destroyed or disabled/enabled. This closes https://github.com/select2/select2/issues/3031.
This commit is contained in:
parent
ef5c88a6f6
commit
b382fdca9c
20
dist/js/select2.amd.full.js
vendored
20
dist/js/select2.amd.full.js
vendored
@ -787,12 +787,16 @@ define('select2/selection/base',[
|
|||||||
|
|
||||||
BaseSelection.prototype.render = function () {
|
BaseSelection.prototype.render = function () {
|
||||||
var $selection = $(
|
var $selection = $(
|
||||||
'<span class="select2-selection" tabindex="0" role="combobox" ' +
|
'<span class="select2-selection" role="combobox" ' +
|
||||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||||
'</span>'
|
'</span>'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._tabindex = this.$element.data('old-tabindex') ||
|
||||||
|
this.$element.attr('tabindex') || 0;
|
||||||
|
|
||||||
$selection.attr('title', this.$element.attr('title'));
|
$selection.attr('title', this.$element.attr('title'));
|
||||||
|
$selection.attr('tabindex', this._tabindex);
|
||||||
|
|
||||||
this.$selection = $selection;
|
this.$selection = $selection;
|
||||||
|
|
||||||
@ -843,7 +847,7 @@ define('select2/selection/base',[
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('enable', function () {
|
container.on('enable', function () {
|
||||||
self.$selection.attr('tabindex', '0');
|
self.$selection.attr('tabindex', self._tabindex);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('disable', function () {
|
container.on('disable', function () {
|
||||||
@ -4146,6 +4150,12 @@ define('select2/core',[
|
|||||||
|
|
||||||
Select2.__super__.constructor.call(this);
|
Select2.__super__.constructor.call(this);
|
||||||
|
|
||||||
|
// Set up the tabindex
|
||||||
|
|
||||||
|
var tabindex = $element.attr('tabindex') || 0;
|
||||||
|
$element.data('old-tabindex', tabindex);
|
||||||
|
$element.attr('tabindex', '-1');
|
||||||
|
|
||||||
// Set up containers and adapters
|
// Set up containers and adapters
|
||||||
|
|
||||||
var DataAdapter = this.options.get('dataAdapter');
|
var DataAdapter = this.options.get('dataAdapter');
|
||||||
@ -4203,10 +4213,6 @@ define('select2/core',[
|
|||||||
// Synchronize any monitored attributes
|
// Synchronize any monitored attributes
|
||||||
this._syncAttributes();
|
this._syncAttributes();
|
||||||
|
|
||||||
this._tabindex = $element.attr('tabindex') || 0;
|
|
||||||
|
|
||||||
$element.attr('tabindex', '-1');
|
|
||||||
|
|
||||||
$element.data('select2', this);
|
$element.data('select2', this);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4595,7 +4601,7 @@ define('select2/core',[
|
|||||||
this._sync = null;
|
this._sync = null;
|
||||||
|
|
||||||
this.$element.off('.select2');
|
this.$element.off('.select2');
|
||||||
this.$element.attr('tabindex', this._tabindex);
|
this.$element.attr('tabindex', this.$element.data('old-tabindex'));
|
||||||
|
|
||||||
this.$element.show();
|
this.$element.show();
|
||||||
this.$element.removeData('select2');
|
this.$element.removeData('select2');
|
||||||
|
20
dist/js/select2.amd.js
vendored
20
dist/js/select2.amd.js
vendored
@ -787,12 +787,16 @@ define('select2/selection/base',[
|
|||||||
|
|
||||||
BaseSelection.prototype.render = function () {
|
BaseSelection.prototype.render = function () {
|
||||||
var $selection = $(
|
var $selection = $(
|
||||||
'<span class="select2-selection" tabindex="0" role="combobox" ' +
|
'<span class="select2-selection" role="combobox" ' +
|
||||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||||
'</span>'
|
'</span>'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._tabindex = this.$element.data('old-tabindex') ||
|
||||||
|
this.$element.attr('tabindex') || 0;
|
||||||
|
|
||||||
$selection.attr('title', this.$element.attr('title'));
|
$selection.attr('title', this.$element.attr('title'));
|
||||||
|
$selection.attr('tabindex', this._tabindex);
|
||||||
|
|
||||||
this.$selection = $selection;
|
this.$selection = $selection;
|
||||||
|
|
||||||
@ -843,7 +847,7 @@ define('select2/selection/base',[
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('enable', function () {
|
container.on('enable', function () {
|
||||||
self.$selection.attr('tabindex', '0');
|
self.$selection.attr('tabindex', self._tabindex);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('disable', function () {
|
container.on('disable', function () {
|
||||||
@ -4146,6 +4150,12 @@ define('select2/core',[
|
|||||||
|
|
||||||
Select2.__super__.constructor.call(this);
|
Select2.__super__.constructor.call(this);
|
||||||
|
|
||||||
|
// Set up the tabindex
|
||||||
|
|
||||||
|
var tabindex = $element.attr('tabindex') || 0;
|
||||||
|
$element.data('old-tabindex', tabindex);
|
||||||
|
$element.attr('tabindex', '-1');
|
||||||
|
|
||||||
// Set up containers and adapters
|
// Set up containers and adapters
|
||||||
|
|
||||||
var DataAdapter = this.options.get('dataAdapter');
|
var DataAdapter = this.options.get('dataAdapter');
|
||||||
@ -4203,10 +4213,6 @@ define('select2/core',[
|
|||||||
// Synchronize any monitored attributes
|
// Synchronize any monitored attributes
|
||||||
this._syncAttributes();
|
this._syncAttributes();
|
||||||
|
|
||||||
this._tabindex = $element.attr('tabindex') || 0;
|
|
||||||
|
|
||||||
$element.attr('tabindex', '-1');
|
|
||||||
|
|
||||||
$element.data('select2', this);
|
$element.data('select2', this);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -4595,7 +4601,7 @@ define('select2/core',[
|
|||||||
this._sync = null;
|
this._sync = null;
|
||||||
|
|
||||||
this.$element.off('.select2');
|
this.$element.off('.select2');
|
||||||
this.$element.attr('tabindex', this._tabindex);
|
this.$element.attr('tabindex', this.$element.data('old-tabindex'));
|
||||||
|
|
||||||
this.$element.show();
|
this.$element.show();
|
||||||
this.$element.removeData('select2');
|
this.$element.removeData('select2');
|
||||||
|
20
dist/js/select2.full.js
vendored
20
dist/js/select2.full.js
vendored
@ -1226,12 +1226,16 @@ define('select2/selection/base',[
|
|||||||
|
|
||||||
BaseSelection.prototype.render = function () {
|
BaseSelection.prototype.render = function () {
|
||||||
var $selection = $(
|
var $selection = $(
|
||||||
'<span class="select2-selection" tabindex="0" role="combobox" ' +
|
'<span class="select2-selection" role="combobox" ' +
|
||||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||||
'</span>'
|
'</span>'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._tabindex = this.$element.data('old-tabindex') ||
|
||||||
|
this.$element.attr('tabindex') || 0;
|
||||||
|
|
||||||
$selection.attr('title', this.$element.attr('title'));
|
$selection.attr('title', this.$element.attr('title'));
|
||||||
|
$selection.attr('tabindex', this._tabindex);
|
||||||
|
|
||||||
this.$selection = $selection;
|
this.$selection = $selection;
|
||||||
|
|
||||||
@ -1282,7 +1286,7 @@ define('select2/selection/base',[
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('enable', function () {
|
container.on('enable', function () {
|
||||||
self.$selection.attr('tabindex', '0');
|
self.$selection.attr('tabindex', self._tabindex);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('disable', function () {
|
container.on('disable', function () {
|
||||||
@ -4585,6 +4589,12 @@ define('select2/core',[
|
|||||||
|
|
||||||
Select2.__super__.constructor.call(this);
|
Select2.__super__.constructor.call(this);
|
||||||
|
|
||||||
|
// Set up the tabindex
|
||||||
|
|
||||||
|
var tabindex = $element.attr('tabindex') || 0;
|
||||||
|
$element.data('old-tabindex', tabindex);
|
||||||
|
$element.attr('tabindex', '-1');
|
||||||
|
|
||||||
// Set up containers and adapters
|
// Set up containers and adapters
|
||||||
|
|
||||||
var DataAdapter = this.options.get('dataAdapter');
|
var DataAdapter = this.options.get('dataAdapter');
|
||||||
@ -4642,10 +4652,6 @@ define('select2/core',[
|
|||||||
// Synchronize any monitored attributes
|
// Synchronize any monitored attributes
|
||||||
this._syncAttributes();
|
this._syncAttributes();
|
||||||
|
|
||||||
this._tabindex = $element.attr('tabindex') || 0;
|
|
||||||
|
|
||||||
$element.attr('tabindex', '-1');
|
|
||||||
|
|
||||||
$element.data('select2', this);
|
$element.data('select2', this);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -5034,7 +5040,7 @@ define('select2/core',[
|
|||||||
this._sync = null;
|
this._sync = null;
|
||||||
|
|
||||||
this.$element.off('.select2');
|
this.$element.off('.select2');
|
||||||
this.$element.attr('tabindex', this._tabindex);
|
this.$element.attr('tabindex', this.$element.data('old-tabindex'));
|
||||||
|
|
||||||
this.$element.show();
|
this.$element.show();
|
||||||
this.$element.removeData('select2');
|
this.$element.removeData('select2');
|
||||||
|
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
20
dist/js/select2.js
vendored
20
dist/js/select2.js
vendored
@ -1226,12 +1226,16 @@ define('select2/selection/base',[
|
|||||||
|
|
||||||
BaseSelection.prototype.render = function () {
|
BaseSelection.prototype.render = function () {
|
||||||
var $selection = $(
|
var $selection = $(
|
||||||
'<span class="select2-selection" tabindex="0" role="combobox" ' +
|
'<span class="select2-selection" role="combobox" ' +
|
||||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||||
'</span>'
|
'</span>'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._tabindex = this.$element.data('old-tabindex') ||
|
||||||
|
this.$element.attr('tabindex') || 0;
|
||||||
|
|
||||||
$selection.attr('title', this.$element.attr('title'));
|
$selection.attr('title', this.$element.attr('title'));
|
||||||
|
$selection.attr('tabindex', this._tabindex);
|
||||||
|
|
||||||
this.$selection = $selection;
|
this.$selection = $selection;
|
||||||
|
|
||||||
@ -1282,7 +1286,7 @@ define('select2/selection/base',[
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('enable', function () {
|
container.on('enable', function () {
|
||||||
self.$selection.attr('tabindex', '0');
|
self.$selection.attr('tabindex', self._tabindex);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('disable', function () {
|
container.on('disable', function () {
|
||||||
@ -4585,6 +4589,12 @@ define('select2/core',[
|
|||||||
|
|
||||||
Select2.__super__.constructor.call(this);
|
Select2.__super__.constructor.call(this);
|
||||||
|
|
||||||
|
// Set up the tabindex
|
||||||
|
|
||||||
|
var tabindex = $element.attr('tabindex') || 0;
|
||||||
|
$element.data('old-tabindex', tabindex);
|
||||||
|
$element.attr('tabindex', '-1');
|
||||||
|
|
||||||
// Set up containers and adapters
|
// Set up containers and adapters
|
||||||
|
|
||||||
var DataAdapter = this.options.get('dataAdapter');
|
var DataAdapter = this.options.get('dataAdapter');
|
||||||
@ -4642,10 +4652,6 @@ define('select2/core',[
|
|||||||
// Synchronize any monitored attributes
|
// Synchronize any monitored attributes
|
||||||
this._syncAttributes();
|
this._syncAttributes();
|
||||||
|
|
||||||
this._tabindex = $element.attr('tabindex') || 0;
|
|
||||||
|
|
||||||
$element.attr('tabindex', '-1');
|
|
||||||
|
|
||||||
$element.data('select2', this);
|
$element.data('select2', this);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -5034,7 +5040,7 @@ define('select2/core',[
|
|||||||
this._sync = null;
|
this._sync = null;
|
||||||
|
|
||||||
this.$element.off('.select2');
|
this.$element.off('.select2');
|
||||||
this.$element.attr('tabindex', this._tabindex);
|
this.$element.attr('tabindex', this.$element.data('old-tabindex'));
|
||||||
|
|
||||||
this.$element.show();
|
this.$element.show();
|
||||||
this.$element.removeData('select2');
|
this.$element.removeData('select2');
|
||||||
|
4
dist/js/select2.min.js
vendored
4
dist/js/select2.min.js
vendored
File diff suppressed because one or more lines are too long
12
src/js/select2/core.js
vendored
12
src/js/select2/core.js
vendored
@ -19,6 +19,12 @@ define([
|
|||||||
|
|
||||||
Select2.__super__.constructor.call(this);
|
Select2.__super__.constructor.call(this);
|
||||||
|
|
||||||
|
// Set up the tabindex
|
||||||
|
|
||||||
|
var tabindex = $element.attr('tabindex') || 0;
|
||||||
|
$element.data('old-tabindex', tabindex);
|
||||||
|
$element.attr('tabindex', '-1');
|
||||||
|
|
||||||
// Set up containers and adapters
|
// Set up containers and adapters
|
||||||
|
|
||||||
var DataAdapter = this.options.get('dataAdapter');
|
var DataAdapter = this.options.get('dataAdapter');
|
||||||
@ -76,10 +82,6 @@ define([
|
|||||||
// Synchronize any monitored attributes
|
// Synchronize any monitored attributes
|
||||||
this._syncAttributes();
|
this._syncAttributes();
|
||||||
|
|
||||||
this._tabindex = $element.attr('tabindex') || 0;
|
|
||||||
|
|
||||||
$element.attr('tabindex', '-1');
|
|
||||||
|
|
||||||
$element.data('select2', this);
|
$element.data('select2', this);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -468,7 +470,7 @@ define([
|
|||||||
this._sync = null;
|
this._sync = null;
|
||||||
|
|
||||||
this.$element.off('.select2');
|
this.$element.off('.select2');
|
||||||
this.$element.attr('tabindex', this._tabindex);
|
this.$element.attr('tabindex', this.$element.data('old-tabindex'));
|
||||||
|
|
||||||
this.$element.show();
|
this.$element.show();
|
||||||
this.$element.removeData('select2');
|
this.$element.removeData('select2');
|
||||||
|
8
src/js/select2/selection/base.js
vendored
8
src/js/select2/selection/base.js
vendored
@ -14,12 +14,16 @@ define([
|
|||||||
|
|
||||||
BaseSelection.prototype.render = function () {
|
BaseSelection.prototype.render = function () {
|
||||||
var $selection = $(
|
var $selection = $(
|
||||||
'<span class="select2-selection" tabindex="0" role="combobox" ' +
|
'<span class="select2-selection" role="combobox" ' +
|
||||||
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
'aria-autocomplete="list" aria-haspopup="true" aria-expanded="false">' +
|
||||||
'</span>'
|
'</span>'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this._tabindex = this.$element.data('old-tabindex') ||
|
||||||
|
this.$element.attr('tabindex') || 0;
|
||||||
|
|
||||||
$selection.attr('title', this.$element.attr('title'));
|
$selection.attr('title', this.$element.attr('title'));
|
||||||
|
$selection.attr('tabindex', this._tabindex);
|
||||||
|
|
||||||
this.$selection = $selection;
|
this.$selection = $selection;
|
||||||
|
|
||||||
@ -70,7 +74,7 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.on('enable', function () {
|
container.on('enable', function () {
|
||||||
self.$selection.attr('tabindex', '0');
|
self.$selection.attr('tabindex', self._tabindex);
|
||||||
});
|
});
|
||||||
|
|
||||||
container.on('disable', function () {
|
container.on('disable', function () {
|
||||||
|
@ -123,6 +123,39 @@ test('the container should be in the tab order', function (assert) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('a custom tabindex is copied', function (assert) {
|
||||||
|
var $select = $('#qunit-fixture .single');
|
||||||
|
$select.attr('tabindex', '999');
|
||||||
|
|
||||||
|
var selection = new BaseSelection($select, options);
|
||||||
|
var $selection = selection.render();
|
||||||
|
|
||||||
|
var container = new MockContainer();
|
||||||
|
selection.bind(container, $('<span></span>'));
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
$selection.attr('tabindex'),
|
||||||
|
'999',
|
||||||
|
'The tab index should match the original tab index'
|
||||||
|
);
|
||||||
|
|
||||||
|
container.trigger('disable');
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
$selection.attr('tabindex'),
|
||||||
|
'-1',
|
||||||
|
'The selection should be dropped out of the tab order when disabled'
|
||||||
|
);
|
||||||
|
|
||||||
|
container.trigger('enable');
|
||||||
|
|
||||||
|
assert.equal(
|
||||||
|
$selection.attr('tabindex'),
|
||||||
|
'999',
|
||||||
|
'The tab index should be restored when re-enabled'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
module('Accessibility - Single');
|
module('Accessibility - Single');
|
||||||
|
|
||||||
test('aria-labelledby should match the rendered container', function (assert) {
|
test('aria-labelledby should match the rendered container', function (assert) {
|
||||||
|
@ -7,19 +7,19 @@
|
|||||||
<body>
|
<body>
|
||||||
<div id="qunit"></div>
|
<div id="qunit"></div>
|
||||||
<div id="qunit-fixture">
|
<div id="qunit-fixture">
|
||||||
<select class="single" title="This is an example title">
|
<select class="single" title="This is an example title" tabindex="0">
|
||||||
<option value="default">Default</option>
|
<option value="default">Default</option>
|
||||||
<option value="1">One</option>
|
<option value="1">One</option>
|
||||||
<option>2</option>
|
<option>2</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select class="multiple" multiple="multiple" title="One more example title">
|
<select class="multiple" multiple="multiple" title="One more example title" tabindex="0">
|
||||||
<option value="default">Default</option>
|
<option value="default">Default</option>
|
||||||
<option value="1">One</option>
|
<option value="1">One</option>
|
||||||
<option>2</option>
|
<option>2</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<select class="groups">
|
<select class="groups" tabindex="0">
|
||||||
<optgroup label="Test">
|
<optgroup label="Test">
|
||||||
<option value="one">One</option>
|
<option value="one">One</option>
|
||||||
<option value="two">Two</option>
|
<option value="two">Two</option>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user