Skip to content

Commit adb126e

Browse files
committed
Merge pull request select2#1239 from NielsKuhnel/master
Keyboard navigation in choices (whitespace preserved)
2 parents 55827b9 + 870b1b7 commit adb126e

2 files changed

Lines changed: 95 additions & 17 deletions

File tree

select2.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
Version: @@ver@@ Timestamp: @@timestamp@@
33
*/
44
.select2-container {

select2.js

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/*
1+
/*
22
Copyright 2012 Igor Vaynberg
33
44
Version: @@ver@@ Timestamp: @@timestamp@@
@@ -262,6 +262,23 @@ the specific language governing permissions and limitations under the Apache Lic
262262
}, 0);
263263
}
264264

265+
function getCursorInfo(el) {
266+
el = $(el)[0];
267+
var offset = 0;
268+
var length = 0;
269+
if ('selectionStart' in el) {
270+
offset = el.selectionStart;
271+
length = el.selectionEnd - offset;
272+
} else if ('selection' in document) {
273+
el.focus();
274+
var sel = document.selection.createRange();
275+
length = document.selection.createRange().text.length;
276+
sel.moveStart('character', -el.value.length);
277+
offset = sel.text.length - length;
278+
}
279+
return { offset: offset, length: length };
280+
}
281+
265282
function killEvent(event) {
266283
event.preventDefault();
267284
event.stopPropagation();
@@ -1261,7 +1278,9 @@ the specific language governing permissions and limitations under the Apache Lic
12611278
this.dropdown.hide();
12621279
this.container.removeClass("select2-dropdown-open");
12631280
this.results.empty();
1264-
this.clearSearch();
1281+
1282+
1283+
this.clearSearch();
12651284
this.search.removeClass("select2-active");
12661285
this.opts.element.trigger($.Event("close"));
12671286
},
@@ -2196,6 +2215,24 @@ the specific language governing permissions and limitations under the Apache Lic
21962215
return opts;
21972216
},
21982217

2218+
selectChoice: function (choice) {
2219+
2220+
var selected = this.container.find(".select2-search-choice-focus");
2221+
if (selected.length && choice && choice[0] == selected[0]) {
2222+
2223+
} else {
2224+
if (selected.length) {
2225+
this.opts.element.trigger("choice-deselected", selected);
2226+
}
2227+
selected.removeClass("select2-search-choice-focus");
2228+
if (choice && choice.length) {
2229+
this.close();
2230+
choice.addClass("select2-search-choice-focus");
2231+
this.opts.element.trigger("choice-selected", choice);
2232+
}
2233+
}
2234+
},
2235+
21992236
// multi
22002237
initContainer: function () {
22012238

@@ -2204,6 +2241,18 @@ the specific language governing permissions and limitations under the Apache Lic
22042241
this.searchContainer = this.container.find(".select2-search-field");
22052242
this.selection = selection = this.container.find(selector);
22062243

2244+
var _this = this;
2245+
this.selection.on("mousedown", ".select2-search-choice", function (e) {
2246+
//killEvent(e);
2247+
_this.search[0].focus();
2248+
_this.selectChoice($(this));
2249+
})
2250+
//.sortable({
2251+
// items: " > li",
2252+
// tolerance: "pointer",
2253+
// revert: 100
2254+
//});
2255+
22072256
// rewrite labels from original element to focusser
22082257
this.search.attr("id", "s2id_autogen"+nextUid());
22092258
$("label[for='" + this.opts.element.attr("id") + "']")
@@ -2218,27 +2267,51 @@ the specific language governing permissions and limitations under the Apache Lic
22182267

22192268
this.search.attr("tabindex", this.elementTabIndex);
22202269

2270+
this.keydowns = 0;
22212271
this.search.bind("keydown", this.bind(function (e) {
22222272
if (!this.isInterfaceEnabled()) return;
22232273

2224-
if (e.which === KEY.BACKSPACE && this.search.val() === "") {
2225-
this.close();
2226-
2227-
var choices,
2228-
selected = selection.find(".select2-search-choice-focus");
2229-
if (selected.length > 0) {
2274+
++this.keydowns;
2275+
var selected = selection.find(".select2-search-choice-focus");
2276+
var prev = selected.prev(".select2-search-choice:not(.select2-locked)");
2277+
var next = selected.next(".select2-search-choice:not(.select2-locked)");
2278+
var pos = getCursorInfo(this.search);
2279+
2280+
if (selected.length &&
2281+
(e.which == KEY.LEFT || e.which == KEY.RIGHT || e.which == KEY.BACKSPACE || e.which == KEY.DELETE || e.which == KEY.ENTER)) {
2282+
var selectedChoice = selected;
2283+
if (e.which == KEY.LEFT && prev.length) {
2284+
selectedChoice = prev;
2285+
}
2286+
else if (e.which == KEY.RIGHT) {
2287+
selectedChoice = next.length ? next : null;
2288+
}
2289+
else if (e.which === KEY.BACKSPACE) {
22302290
this.unselect(selected.first());
22312291
this.search.width(10);
2232-
killEvent(e);
2233-
return;
2292+
selectedChoice = prev.length ? prev : next;
2293+
} else if (e.which == KEY.DELETE) {
2294+
this.unselect(selected.first());
2295+
this.search.width(10);
2296+
selectedChoice = next.length ? next : null;
2297+
} else if (e.which == KEY.ENTER) {
2298+
selectedChoice = null;
22342299
}
22352300

2236-
choices = selection.find(".select2-search-choice:not(.select2-locked)");
2237-
if (choices.length > 0) {
2238-
choices.last().addClass("select2-search-choice-focus");
2301+
this.selectChoice(selectedChoice);
2302+
killEvent(e);
2303+
if (!selectedChoice || !selectedChoice.length) {
2304+
this.open();
22392305
}
2306+
return;
2307+
} else if (((e.which === KEY.BACKSPACE && this.keydowns == 1)
2308+
|| e.which == KEY.LEFT) && (pos.offset == 0 && !pos.length)) {
2309+
2310+
this.selectChoice(selection.find(".select2-search-choice:not(.select2-locked)").last());
2311+
killEvent(e);
2312+
return;
22402313
} else {
2241-
selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
2314+
this.selectChoice(null);
22422315
}
22432316

22442317
if (this.opened()) {
@@ -2287,12 +2360,16 @@ the specific language governing permissions and limitations under the Apache Lic
22872360

22882361
}));
22892362

2290-
this.search.bind("keyup", this.bind(this.resizeSearch));
2363+
this.search.bind("keyup", this.bind(function (e) {
2364+
this.keydowns = 0;
2365+
this.resizeSearch();
2366+
})
2367+
);
22912368

22922369
this.search.bind("blur", this.bind(function(e) {
22932370
this.container.removeClass("select2-container-active");
22942371
this.search.removeClass("select2-focused");
2295-
this.selection.find(".select2-search-choice-focus").removeClass("select2-search-choice-focus");
2372+
this.selectChoice(null);
22962373
if (!this.opened()) this.clearSearch();
22972374
e.stopImmediatePropagation();
22982375
}));
@@ -2303,6 +2380,7 @@ the specific language governing permissions and limitations under the Apache Lic
23032380
// clicked inside a select2 search choice, do not open
23042381
return;
23052382
}
2383+
this.selectChoice(null);
23062384
this.clearPlaceholder();
23072385
this.open();
23082386
this.focusSearch();

0 commit comments

Comments
 (0)