You can manually implement this functionality by listening for keypress events, searching the dropdown list of items, and simulating a selection by triggering the mouseenter event on that item.
- Since it's hard to attach a keypress handler to the dynamic
<span>
that Select2 generates. We can listen to keypress events on the body that occured on a contain that has both --open
and --focus
- Then we'll need to convert the keypress event char code into a alphanumeric character
- Select2 will automatically handle most special characters like <kbd>āā</kbd> and <kbd>Enter</kbd>, but we'll proceed if we see a letter like this
/[a-z]/i.test(char)
- HTML Dropdowns searches kind of work in a wonky way. As soon as any character is pressed, it'll find that element, but you can also string together multiple key in quick succession to narrow down the focus even more. So we'll set a delayed timeout that will refresh the clock every time it's called, but will clear the temporary search string after 750 ms.
- Then we'll crosswalk from the current element to find the
$results
dropdown and all of the .select2-results__option
items inside it.
- Starting from the top, we'll test each one to see if the start of the option text is a case insensitive match for our current search string
- If so, we'll fire the mouseenter event, which is used elsewhere to highlight result items
All told, it'll look like this:
<!-- language: lang-js -->
// https://stackoverflow.com/a/1909508/1366033
var delay = (function () {
var timer = 0;
return function (callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
// if we have focus and the container is open, it means we're not in the search field
$("body").on('keydown', '.select2-container--focus.select2-container--open',
function(e) {
// get char https://stackoverflow.com/a/34711175/1366033
var char = e.key
// only accept a range of characters
if (/^[a-z]{1}$/i.test(char)) {
// select2 handles others https://github.com/select2/select2/blob/4.0.5/dist/js/select2.js#L5368
var $this = $(this); // need inside delay
// act immediately but temporarily store a run of characters
delay(function () {
// clear search text on delay
$this.data("searchText", "");
}, 750);
// build search string with any previous characters and store new value
var previous = $this.data("searchText");
var searchText = (previous === undefined ? "" : previous) + char;
$this.data("searchText", searchText);
console.log(searchText);
// find available options
var $sel2 = $this.data("element");
var $select2 = $sel2.data("select2");
var $results = $select2.results.$results;
var $opts = $results.find(".select2-results__option");
// find first option with matching start and select it
$opts.each(function(i,el) {
var $opt = $(el)
var optText = $opt.text()
var startsWithRegex = new RegExp('^' + searchText, 'i');
var match = startsWithRegex.test(optText)
// if we got one - fire event and we can leave
if (match) {
// mouseenter is a reliable way to highlight item in results list
// https://github.com/select2/select2/blob/4.0.5/dist/js/select2.js#L1234
$opt.trigger('mouseenter');
return;
}
// if we found nothing, previous selection will stay highlighted
})
}
});
Working Demo in StackSnippets & jsFiddle:
<!-- begin snippet: js hide: true console: true babel: false -->
<!-- language: lang-js -->
$('#select2').select2({
minimumResultsForSearch: 20,
});
$('#select2search').select2();
// https://stackoverflow.com/a/1909508/1366033
var delay = (function () {
var timer = 0;
return function (callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
// if we have focus and the container is open, it means we're not in the search field
$("body").on('keydown', '.select2-container--focus.select2-container--open',
function(e) {
// get char https://stackoverflow.com/a/34711175/1366033
var char = e.key
// only accept a range of characters
if (/^[a-z]{1}$/i.test(char)) {
// select2 handles others https://github.com/select2/select2/blob/4.0.5/dist/js/select2.js#L5368
var $this = $(this); // need inside delay
// act immediately but temporarily store a run of characters
delay(function () {
// clear search text on delay
$this.data("searchText", "");
}, 750);
// build search string with any previous characters and store new value
var previous = $this.data("searchText");
var searchText = (previous === undefined ? "" : previous) + char;
$this.data("searchText", searchText);
console.log(searchText);
// find available options
var $sel2 = $this.data("element");
var $select2 = $sel2.data("select2");
var $results = $select2.results.$results;
var $opts = $results.find(".select2-results__option");
// find first option with matching start and select it
$opts.each(function(i,el) {
var $opt = $(el)
var optText = $opt.text()
var startsWithRegex = new RegExp('^' + searchText, 'i');
var match = startsWithRegex.test(optText)
// if we got one - fire event and we can leave
if (match) {
// mouseenter is a reliable way to highlight item in results list
// https://github.com/select2/select2/blob/4.0.5/dist/js/select2.js#L1234
$opt.trigger('mouseenter');
return;
}
// if we found nothing, previous selection will stay highlighted
})
}
});
<!-- language: lang-css -->
.form-control {
padding:15px;
}
label {
display:inline-block;
width:150px;
}
select {
width: 150px;
border: 1px solid #aaa;
border-radius: 4px;
height: 28px;
}
<!-- language: lang-html -->
<link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.css" rel="stylesheet"/>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.js"></script>
<div class="form-control">
<label for="normal">Normal</label>
<select id="normal" >
<option value="1">Apple</option>
<option value="2">Banana</option>
<option value="3">Carrot</option>
<option value="4">Donut</option>
</select>
</div>
<div class="form-control">
<label for="select2">Select2</label>
<select id="select2" >
<option value="1">Apple</option>
<option value="2">Banana</option>
<option value="3">Carrot</option>
<option value="4">Donut</option>
</select>
</div>
<div class="form-control">
<label for="select2search">Select2 + Search</label>
<select id="select2search" >
<option value="1">Apple</option>
<option value="2">Banana</option>
<option value="3">Carrot</option>
<option value="4">Donut</option>
</select>
</div>
<!-- end snippet -->
Working Demo