Skip to content

Commit

Permalink
More intuitive handling of the enter key
Browse files Browse the repository at this point in the history
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
#3036 (comment).

[1]: http://superuser.com/q/78891/72528
[2]: https://developer.gnome.org/gtk3/stable/GtkIconView.html#GtkIconView-toggle-cursor-item
  • Loading branch information
kevin-brown committed Mar 2, 2015
1 parent c0839b4 commit 017c201
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 75 deletions.
44 changes: 30 additions & 14 deletions dist/js/select2.amd.full.js
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,16 @@ define('select2/results',[
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 () {
var $highlighted = self.getHighlightedResults();

Expand All @@ -537,13 +547,7 @@ define('select2/results',[
var data = $highlighted.data('data');

if ($highlighted.attr('aria-selected') == 'true') {
if (self.options.get('multiple')) {
self.trigger('unselect', {
data: data
});
} else {
self.trigger('close');
}
self.trigger('close');
} else {
self.trigger('select', {
data: data
Expand Down Expand Up @@ -3647,17 +3651,25 @@ define('select2/dropdown/closeOnSelect',[
decorated.call(this, container, $container);

container.on('select', function (evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}
self._selectTriggered(evt);
});

self.trigger('close');
container.on('unselect', function (evt) {
self._selectTriggered(evt);
});
};

CloseOnSelect.prototype._selectTriggered = function (_, evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}

this.trigger('close');
};

return CloseOnSelect;
});

Expand Down Expand Up @@ -4483,6 +4495,10 @@ define('select2/core',[
if (key === KEYS.ENTER) {
self.trigger('results:select');

evt.preventDefault();
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
self.trigger('results:toggle');

evt.preventDefault();
} else if (key === KEYS.UP) {
self.trigger('results:previous');
Expand Down
44 changes: 30 additions & 14 deletions dist/js/select2.amd.js
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,16 @@ define('select2/results',[
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 () {
var $highlighted = self.getHighlightedResults();

Expand All @@ -537,13 +547,7 @@ define('select2/results',[
var data = $highlighted.data('data');

if ($highlighted.attr('aria-selected') == 'true') {
if (self.options.get('multiple')) {
self.trigger('unselect', {
data: data
});
} else {
self.trigger('close');
}
self.trigger('close');
} else {
self.trigger('select', {
data: data
Expand Down Expand Up @@ -3647,17 +3651,25 @@ define('select2/dropdown/closeOnSelect',[
decorated.call(this, container, $container);

container.on('select', function (evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}
self._selectTriggered(evt);
});

self.trigger('close');
container.on('unselect', function (evt) {
self._selectTriggered(evt);
});
};

CloseOnSelect.prototype._selectTriggered = function (_, evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}

this.trigger('close');
};

return CloseOnSelect;
});

Expand Down Expand Up @@ -4483,6 +4495,10 @@ define('select2/core',[
if (key === KEYS.ENTER) {
self.trigger('results:select');

evt.preventDefault();
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
self.trigger('results:toggle');

evt.preventDefault();
} else if (key === KEYS.UP) {
self.trigger('results:previous');
Expand Down
44 changes: 30 additions & 14 deletions dist/js/select2.full.js
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,16 @@ define('select2/results',[
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 () {
var $highlighted = self.getHighlightedResults();

Expand All @@ -976,13 +986,7 @@ define('select2/results',[
var data = $highlighted.data('data');

if ($highlighted.attr('aria-selected') == 'true') {
if (self.options.get('multiple')) {
self.trigger('unselect', {
data: data
});
} else {
self.trigger('close');
}
self.trigger('close');
} else {
self.trigger('select', {
data: data
Expand Down Expand Up @@ -4086,17 +4090,25 @@ define('select2/dropdown/closeOnSelect',[
decorated.call(this, container, $container);

container.on('select', function (evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}
self._selectTriggered(evt);
});

self.trigger('close');
container.on('unselect', function (evt) {
self._selectTriggered(evt);
});
};

CloseOnSelect.prototype._selectTriggered = function (_, evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}

this.trigger('close');
};

return CloseOnSelect;
});

Expand Down Expand Up @@ -4922,6 +4934,10 @@ define('select2/core',[
if (key === KEYS.ENTER) {
self.trigger('results:select');

evt.preventDefault();
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
self.trigger('results:toggle');

evt.preventDefault();
} else if (key === KEYS.UP) {
self.trigger('results:previous');
Expand Down
6 changes: 3 additions & 3 deletions dist/js/select2.full.min.js

Large diffs are not rendered by default.

44 changes: 30 additions & 14 deletions dist/js/select2.js
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,16 @@ define('select2/results',[
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 () {
var $highlighted = self.getHighlightedResults();

Expand All @@ -976,13 +986,7 @@ define('select2/results',[
var data = $highlighted.data('data');

if ($highlighted.attr('aria-selected') == 'true') {
if (self.options.get('multiple')) {
self.trigger('unselect', {
data: data
});
} else {
self.trigger('close');
}
self.trigger('close');
} else {
self.trigger('select', {
data: data
Expand Down Expand Up @@ -4086,17 +4090,25 @@ define('select2/dropdown/closeOnSelect',[
decorated.call(this, container, $container);

container.on('select', function (evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}
self._selectTriggered(evt);
});

self.trigger('close');
container.on('unselect', function (evt) {
self._selectTriggered(evt);
});
};

CloseOnSelect.prototype._selectTriggered = function (_, evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}

this.trigger('close');
};

return CloseOnSelect;
});

Expand Down Expand Up @@ -4922,6 +4934,10 @@ define('select2/core',[
if (key === KEYS.ENTER) {
self.trigger('results:select');

evt.preventDefault();
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
self.trigger('results:toggle');

evt.preventDefault();
} else if (key === KEYS.UP) {
self.trigger('results:previous');
Expand Down
4 changes: 2 additions & 2 deletions dist/js/select2.min.js

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions src/js/select2/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,10 @@ define([
if (key === KEYS.ENTER) {
self.trigger('results:select');

evt.preventDefault();
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
self.trigger('results:toggle');

evt.preventDefault();
} else if (key === KEYS.UP) {
self.trigger('results:previous');
Expand Down
22 changes: 15 additions & 7 deletions src/js/select2/dropdown/closeOnSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,24 @@ define([
decorated.call(this, container, $container);

container.on('select', function (evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}
self._selectTriggered(evt);
});

self.trigger('close');
container.on('unselect', function (evt) {
self._selectTriggered(evt);
});
};

CloseOnSelect.prototype._selectTriggered = function (_, evt) {
var originalEvent = evt.originalEvent;

// Don't close if the control key is being held
if (originalEvent && originalEvent.ctrlKey) {
return;
}

this.trigger('close');
};

return CloseOnSelect;
});
18 changes: 11 additions & 7 deletions src/js/select2/results.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,16 @@ define([
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 () {
var $highlighted = self.getHighlightedResults();

Expand All @@ -291,13 +301,7 @@ define([
var data = $highlighted.data('data');

if ($highlighted.attr('aria-selected') == 'true') {
if (self.options.get('multiple')) {
self.trigger('unselect', {
data: data
});
} else {
self.trigger('close');
}
self.trigger('close');
} else {
self.trigger('select', {
data: data
Expand Down

2 comments on commit 017c201

@rubal404
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am using select2 4.0.0 version. When using keyboard navigation on multi select dropdown, I am facing an issue in which able to select an option using enter key or ctrl + space. But once an option is selected, select dropdown loses focus and I have to tab again to bring focus on it for selecting other options.

@kevin-brown
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That issue should have been fixed in 4.0.1. If it wasn't, can you open up a ticket about it?

Please sign in to comment.