More intuitive handling of the enter key
Previously, when in results the enter key would select items that were highlighted if they were not already selected. In the case of a select where multiple items could be selected, pressing enter when highlighting a selected item would also allow it to be unselected. While this seems intuitive for accessibility purposes, the enter button essentially working as a toggle, it caused some really strange behavior. - If the enter button was held down, all previously selected items would be unselected. - The enter button did not work the same across both single and multiple selects. After listening to user feedback, I have decided to remove the "enter as toggle" functionality from Select2 and have gone back to just having the enter button select items. This means that instead of unselected items that are already selected and highlighted, Select2 will just close the dropdown. This is the same as what Select2 would previously do for single selects, so the keyboard functionality is now the same across both. Because this removed the only easy way to unselect items in the dropdown using the keyboard, we had to maintain the toggle functionality. We decided to implement the toggle functionality on the CTRL + Space keybinding, which is in line with other applications. Now when pressing CTRL + Space at the same time in the dropdown, the highlighted result will behave the same as if the mouse selected it, which will toggle the current item in multiple select mode and close the dropdown in single select mode. This is the same keybinding that Windows Explorer [1] and GTK [2] use for toggling the current selection, which was why it was picked. This also fixes an issue where keyboard focus would be lost once an item was unselected from the results. This was due to a bug in the CloseOnSelect module that would only automatically close the dropdown when an item was selected, but not when an item was unselected. Now the dropdown will be closed automatically when an item is unselected, which will also cause the selection (and eventually the search) to be focused. This fixes two issues described in https://github.com/select2/select2/issues/3036#issuecomment-76321411. [1]: http://superuser.com/q/78891/72528 [2]: https://developer.gnome.org/gtk3/stable/GtkIconView.html#GtkIconView-toggle-cursor-item
This commit is contained in:
parent
c0839b4f93
commit
017c201094
32
dist/js/select2.amd.full.js
vendored
32
dist/js/select2.amd.full.js
vendored
@ -527,6 +527,16 @@ define('select2/results',[
|
|||||||
self.$results.removeAttr('aria-activedescendant');
|
self.$results.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
container.on('results:toggle', function () {
|
||||||
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
|
if ($highlighted.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$highlighted.trigger('mouseup');
|
||||||
|
});
|
||||||
|
|
||||||
container.on('results:select', function () {
|
container.on('results:select', function () {
|
||||||
var $highlighted = self.getHighlightedResults();
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
@ -537,13 +547,7 @@ define('select2/results',[
|
|||||||
var data = $highlighted.data('data');
|
var data = $highlighted.data('data');
|
||||||
|
|
||||||
if ($highlighted.attr('aria-selected') == 'true') {
|
if ($highlighted.attr('aria-selected') == 'true') {
|
||||||
if (self.options.get('multiple')) {
|
|
||||||
self.trigger('unselect', {
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.trigger('close');
|
self.trigger('close');
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.trigger('select', {
|
self.trigger('select', {
|
||||||
data: data
|
data: data
|
||||||
@ -3647,6 +3651,15 @@ define('select2/dropdown/closeOnSelect',[
|
|||||||
decorated.call(this, container, $container);
|
decorated.call(this, container, $container);
|
||||||
|
|
||||||
container.on('select', function (evt) {
|
container.on('select', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.on('unselect', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CloseOnSelect.prototype._selectTriggered = function (_, evt) {
|
||||||
var originalEvent = evt.originalEvent;
|
var originalEvent = evt.originalEvent;
|
||||||
|
|
||||||
// Don't close if the control key is being held
|
// Don't close if the control key is being held
|
||||||
@ -3654,8 +3667,7 @@ define('select2/dropdown/closeOnSelect',[
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.trigger('close');
|
this.trigger('close');
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return CloseOnSelect;
|
return CloseOnSelect;
|
||||||
@ -4483,6 +4495,10 @@ define('select2/core',[
|
|||||||
if (key === KEYS.ENTER) {
|
if (key === KEYS.ENTER) {
|
||||||
self.trigger('results:select');
|
self.trigger('results:select');
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
|
||||||
|
self.trigger('results:toggle');
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
} else if (key === KEYS.UP) {
|
} else if (key === KEYS.UP) {
|
||||||
self.trigger('results:previous');
|
self.trigger('results:previous');
|
||||||
|
32
dist/js/select2.amd.js
vendored
32
dist/js/select2.amd.js
vendored
@ -527,6 +527,16 @@ define('select2/results',[
|
|||||||
self.$results.removeAttr('aria-activedescendant');
|
self.$results.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
container.on('results:toggle', function () {
|
||||||
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
|
if ($highlighted.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$highlighted.trigger('mouseup');
|
||||||
|
});
|
||||||
|
|
||||||
container.on('results:select', function () {
|
container.on('results:select', function () {
|
||||||
var $highlighted = self.getHighlightedResults();
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
@ -537,13 +547,7 @@ define('select2/results',[
|
|||||||
var data = $highlighted.data('data');
|
var data = $highlighted.data('data');
|
||||||
|
|
||||||
if ($highlighted.attr('aria-selected') == 'true') {
|
if ($highlighted.attr('aria-selected') == 'true') {
|
||||||
if (self.options.get('multiple')) {
|
|
||||||
self.trigger('unselect', {
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.trigger('close');
|
self.trigger('close');
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.trigger('select', {
|
self.trigger('select', {
|
||||||
data: data
|
data: data
|
||||||
@ -3647,6 +3651,15 @@ define('select2/dropdown/closeOnSelect',[
|
|||||||
decorated.call(this, container, $container);
|
decorated.call(this, container, $container);
|
||||||
|
|
||||||
container.on('select', function (evt) {
|
container.on('select', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.on('unselect', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CloseOnSelect.prototype._selectTriggered = function (_, evt) {
|
||||||
var originalEvent = evt.originalEvent;
|
var originalEvent = evt.originalEvent;
|
||||||
|
|
||||||
// Don't close if the control key is being held
|
// Don't close if the control key is being held
|
||||||
@ -3654,8 +3667,7 @@ define('select2/dropdown/closeOnSelect',[
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.trigger('close');
|
this.trigger('close');
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return CloseOnSelect;
|
return CloseOnSelect;
|
||||||
@ -4483,6 +4495,10 @@ define('select2/core',[
|
|||||||
if (key === KEYS.ENTER) {
|
if (key === KEYS.ENTER) {
|
||||||
self.trigger('results:select');
|
self.trigger('results:select');
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
|
||||||
|
self.trigger('results:toggle');
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
} else if (key === KEYS.UP) {
|
} else if (key === KEYS.UP) {
|
||||||
self.trigger('results:previous');
|
self.trigger('results:previous');
|
||||||
|
32
dist/js/select2.full.js
vendored
32
dist/js/select2.full.js
vendored
@ -966,6 +966,16 @@ define('select2/results',[
|
|||||||
self.$results.removeAttr('aria-activedescendant');
|
self.$results.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
container.on('results:toggle', function () {
|
||||||
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
|
if ($highlighted.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$highlighted.trigger('mouseup');
|
||||||
|
});
|
||||||
|
|
||||||
container.on('results:select', function () {
|
container.on('results:select', function () {
|
||||||
var $highlighted = self.getHighlightedResults();
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
@ -976,13 +986,7 @@ define('select2/results',[
|
|||||||
var data = $highlighted.data('data');
|
var data = $highlighted.data('data');
|
||||||
|
|
||||||
if ($highlighted.attr('aria-selected') == 'true') {
|
if ($highlighted.attr('aria-selected') == 'true') {
|
||||||
if (self.options.get('multiple')) {
|
|
||||||
self.trigger('unselect', {
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.trigger('close');
|
self.trigger('close');
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.trigger('select', {
|
self.trigger('select', {
|
||||||
data: data
|
data: data
|
||||||
@ -4086,6 +4090,15 @@ define('select2/dropdown/closeOnSelect',[
|
|||||||
decorated.call(this, container, $container);
|
decorated.call(this, container, $container);
|
||||||
|
|
||||||
container.on('select', function (evt) {
|
container.on('select', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.on('unselect', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CloseOnSelect.prototype._selectTriggered = function (_, evt) {
|
||||||
var originalEvent = evt.originalEvent;
|
var originalEvent = evt.originalEvent;
|
||||||
|
|
||||||
// Don't close if the control key is being held
|
// Don't close if the control key is being held
|
||||||
@ -4093,8 +4106,7 @@ define('select2/dropdown/closeOnSelect',[
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.trigger('close');
|
this.trigger('close');
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return CloseOnSelect;
|
return CloseOnSelect;
|
||||||
@ -4922,6 +4934,10 @@ define('select2/core',[
|
|||||||
if (key === KEYS.ENTER) {
|
if (key === KEYS.ENTER) {
|
||||||
self.trigger('results:select');
|
self.trigger('results:select');
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
|
||||||
|
self.trigger('results:toggle');
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
} else if (key === KEYS.UP) {
|
} else if (key === KEYS.UP) {
|
||||||
self.trigger('results:previous');
|
self.trigger('results:previous');
|
||||||
|
6
dist/js/select2.full.min.js
vendored
6
dist/js/select2.full.min.js
vendored
File diff suppressed because one or more lines are too long
32
dist/js/select2.js
vendored
32
dist/js/select2.js
vendored
@ -966,6 +966,16 @@ define('select2/results',[
|
|||||||
self.$results.removeAttr('aria-activedescendant');
|
self.$results.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
container.on('results:toggle', function () {
|
||||||
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
|
if ($highlighted.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$highlighted.trigger('mouseup');
|
||||||
|
});
|
||||||
|
|
||||||
container.on('results:select', function () {
|
container.on('results:select', function () {
|
||||||
var $highlighted = self.getHighlightedResults();
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
@ -976,13 +986,7 @@ define('select2/results',[
|
|||||||
var data = $highlighted.data('data');
|
var data = $highlighted.data('data');
|
||||||
|
|
||||||
if ($highlighted.attr('aria-selected') == 'true') {
|
if ($highlighted.attr('aria-selected') == 'true') {
|
||||||
if (self.options.get('multiple')) {
|
|
||||||
self.trigger('unselect', {
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.trigger('close');
|
self.trigger('close');
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.trigger('select', {
|
self.trigger('select', {
|
||||||
data: data
|
data: data
|
||||||
@ -4086,6 +4090,15 @@ define('select2/dropdown/closeOnSelect',[
|
|||||||
decorated.call(this, container, $container);
|
decorated.call(this, container, $container);
|
||||||
|
|
||||||
container.on('select', function (evt) {
|
container.on('select', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.on('unselect', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CloseOnSelect.prototype._selectTriggered = function (_, evt) {
|
||||||
var originalEvent = evt.originalEvent;
|
var originalEvent = evt.originalEvent;
|
||||||
|
|
||||||
// Don't close if the control key is being held
|
// Don't close if the control key is being held
|
||||||
@ -4093,8 +4106,7 @@ define('select2/dropdown/closeOnSelect',[
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.trigger('close');
|
this.trigger('close');
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return CloseOnSelect;
|
return CloseOnSelect;
|
||||||
@ -4922,6 +4934,10 @@ define('select2/core',[
|
|||||||
if (key === KEYS.ENTER) {
|
if (key === KEYS.ENTER) {
|
||||||
self.trigger('results:select');
|
self.trigger('results:select');
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
|
||||||
|
self.trigger('results:toggle');
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
} else if (key === KEYS.UP) {
|
} else if (key === KEYS.UP) {
|
||||||
self.trigger('results:previous');
|
self.trigger('results:previous');
|
||||||
|
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
4
src/js/select2/core.js
vendored
4
src/js/select2/core.js
vendored
@ -296,6 +296,10 @@ define([
|
|||||||
if (key === KEYS.ENTER) {
|
if (key === KEYS.ENTER) {
|
||||||
self.trigger('results:select');
|
self.trigger('results:select');
|
||||||
|
|
||||||
|
evt.preventDefault();
|
||||||
|
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
|
||||||
|
self.trigger('results:toggle');
|
||||||
|
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
} else if (key === KEYS.UP) {
|
} else if (key === KEYS.UP) {
|
||||||
self.trigger('results:previous');
|
self.trigger('results:previous');
|
||||||
|
12
src/js/select2/dropdown/closeOnSelect.js
vendored
12
src/js/select2/dropdown/closeOnSelect.js
vendored
@ -9,6 +9,15 @@ define([
|
|||||||
decorated.call(this, container, $container);
|
decorated.call(this, container, $container);
|
||||||
|
|
||||||
container.on('select', function (evt) {
|
container.on('select', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.on('unselect', function (evt) {
|
||||||
|
self._selectTriggered(evt);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
CloseOnSelect.prototype._selectTriggered = function (_, evt) {
|
||||||
var originalEvent = evt.originalEvent;
|
var originalEvent = evt.originalEvent;
|
||||||
|
|
||||||
// Don't close if the control key is being held
|
// Don't close if the control key is being held
|
||||||
@ -16,8 +25,7 @@ define([
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.trigger('close');
|
this.trigger('close');
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return CloseOnSelect;
|
return CloseOnSelect;
|
||||||
|
16
src/js/select2/results.js
vendored
16
src/js/select2/results.js
vendored
@ -281,6 +281,16 @@ define([
|
|||||||
self.$results.removeAttr('aria-activedescendant');
|
self.$results.removeAttr('aria-activedescendant');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
container.on('results:toggle', function () {
|
||||||
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
|
if ($highlighted.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$highlighted.trigger('mouseup');
|
||||||
|
});
|
||||||
|
|
||||||
container.on('results:select', function () {
|
container.on('results:select', function () {
|
||||||
var $highlighted = self.getHighlightedResults();
|
var $highlighted = self.getHighlightedResults();
|
||||||
|
|
||||||
@ -291,13 +301,7 @@ define([
|
|||||||
var data = $highlighted.data('data');
|
var data = $highlighted.data('data');
|
||||||
|
|
||||||
if ($highlighted.attr('aria-selected') == 'true') {
|
if ($highlighted.attr('aria-selected') == 'true') {
|
||||||
if (self.options.get('multiple')) {
|
|
||||||
self.trigger('unselect', {
|
|
||||||
data: data
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
self.trigger('close');
|
self.trigger('close');
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
self.trigger('select', {
|
self.trigger('select', {
|
||||||
data: data
|
data: data
|
||||||
|
Loading…
Reference in New Issue
Block a user