Use HTML5 (datalist) autocomplete with 'contains' approach, not just 'starts with'
'contains' approach
Maybe this is what you are looking for (part 1 of your question).
It goes with the limitation of "starts with" and changes when a selection is made.
'use strict';function updateList(that) { if (!that) { return; } var lastValue = that.lastValue, value = that.value, array = [], pos = value.indexOf('|'), start = that.selectionStart, end = that.selectionEnd, options;
if (that.options) { options = that.options; } else { options = Object.keys(that.list.options).map(function (option) { return that.list.options[option].value; }); that.options = options; }
if (lastValue !== value) { that.list.innerHTML = options.filter(function (a) { return ~a.toLowerCase().indexOf(value.toLowerCase()); }).map(function (a) { return '<option value="' + value + '|' + a + '">' + a + '</option>'; }).join(); updateInput(that); that.lastValue = value; }}
function updateInput(that) { if (!that) { return; } var value = that.value, pos = value.indexOf('|'), start = that.selectionStart, end = that.selectionEnd;
if (~pos) { value = value.slice(pos + 1); } that.value = value; that.setSelectionRange(start, end);}
document.getElementsByTagName('input').browser.addEventListener('keyup', function (e) { updateList(this);});document.getElementsByTagName('input').browser.addEventListener('input', function (e) { updateInput(this);});
<input list="browsers" name="browser" id="browser" onkeyup="updateList();" oninput="updateInput();"><datalist id="browsers"> <option value="Internet Explorer"> <option value="Firefox"> <option value="Chrome"> <option value="Opera"> <option value="Safari"></datalist>
How to make datalist match result from beginning only
There is a pretty sound solution for such situations: to leave an empty datalist inner html (if empty, it will be filled on a focus event) and fill it dynamically on every input event with Javascript. In JS, it is better to alphabetically sort the array of options to be able to stop iteration when some coincidences are already found and there's no further matches. Continuing the example of this question:
const dlOptions = ["C", "C#", "C++", "CSS", "Erlang", "Go", "HTML", "Java",
"JavaScript", "PHP And HTML", "Python And C++", "Ruby And Go"].map(o => {
return [`<option value="${o}"></option>`, o.toLowerCase()];
});
function completeDataList(e) {
const fill = val => document.getElementById('languages').innerHTML = val;
if(!e.target.value) {
fill(dlOptions.reduce((sum, [html]) => sum + html, ''));
} else if(!(e instanceof InputEvent)) { // OR: else if(!e.inputType)
e.target.blur();
} else {
const inputValue = e.target.value.toLowerCase();
let result = '';
for (const [html, valuePattern] of dlOptions) {
if (!valuePattern.indexOf(inputValue)) {
result += html;
} else if (result) {
break;
}
}
fill(result);
}
}
function fillDataListIfEmpty() {
if(!document.getElementById('languages').innerHTML) {
completeDataList({ target: {} });
}
}
<h1>Datalist Demo</h1>
<label for="default">Pick a programming language</label>
<input type="text" id="default" list="languages" oninput="completeDataList(event)" onfocus="fillDataListIfEmpty()">
<datalist id="languages"></datalist>
HTML5 datalist does not recognize numbers in autocomplete function
see how datalist works:
If there is only one character in the option and you type it , there
is no reason to show that already typed character in dropdown
Thus if there is
<datalist id="textList">
<option>g</option>
<option>i</option>
<option>s</option>
</datalist>
then type in i
,g
or s
will not populate the list, because it is already typed, the full word
Similar for numbers(datalist will NOT consider numbers and text differently)
<datalist id="textList">
<option>0</option>
<option>1</option>
<option>2</option>
</datalist>
will not work,
Instead try this:
<datalist id="textList">
<option>01</option>
<option>11</option>
<option>22</option>
</datalist>
this will of-course work until you type the complete word which matched an option(at this moment there will be no need to show the same thing in dropdown)
This always happen, for example, your code:
<datalist id="textList">
<option>Greece</option>
<option>Italy</option>
<option>Spain</option>
</datalist>
what happens when you type in Greece
, the whole text fromm option, it disappears, isn't it. so that is exactly what is happening with you when there is just one character in option.
JsFiddle
Show datalist labels but submit the actual value
Note that datalist
is not the same as a select
. It allows users to enter a custom value that is not in the list, and it would be impossible to fetch an alternate value for such input without defining it first.
Possible ways to handle user input are to submit the entered value as is, submit a blank value, or prevent submitting. This answer handles only the first two options.
If you want to disallow user input entirely, maybe select
would be a better choice.
To show only the text value of the option
in the dropdown, we use the inner text for it and leave out the value
attribute. The actual value that we want to send along is stored in a custom data-value
attribute:
To submit this data-value
we have to use an <input type="hidden">
. In this case we leave out the name="answer"
on the regular input and move it to the hidden copy.
<input list="suggestionList" id="answerInput">
<datalist id="suggestionList">
<option data-value="42">The answer</option>
</datalist>
<input type="hidden" name="answer" id="answerInput-hidden">
This way, when the text in the original input changes we can use javascript to check if the text also present in the datalist
and fetch its data-value
. That value is inserted into the hidden input and submitted.
document.querySelector('input[list]').addEventListener('input', function(e) {
var input = e.target,
list = input.getAttribute('list'),
options = document.querySelectorAll('#' + list + ' option'),
hiddenInput = document.getElementById(input.getAttribute('id') + '-hidden'),
inputValue = input.value;
hiddenInput.value = inputValue;
for(var i = 0; i < options.length; i++) {
var option = options[i];
if(option.innerText === inputValue) {
hiddenInput.value = option.getAttribute('data-value');
break;
}
}
});
The id answer
and answer-hidden
on the regular and hidden input are needed for the script to know which input belongs to which hidden version. This way it's possible to have multiple input
s on the same page with one or more datalist
s providing suggestions.
Any user input is submitted as is. To submit an empty value when the user input is not present in the datalist, change hiddenInput.value = inputValue
to hiddenInput.value = ""
Working jsFiddle examples: plain javascript and jQuery
Select intelligently from a drop down list
Suppose you have a datalist like below :
<datalist id="locations">
<option value="India taj mahal">
<option value="India Qutub Minar">
<option value="China gate">
</datalist>
Now you can select all option values and put them in an array like below :
var options = document.getElementById('locations').options;
var values = [];
for(var i=0; i<options.length; i++)
{
values.push(options[i].value);
}
Now let's suppose your search term is India Minar, so you can build a search logic something like below :
var searchTerm = "India Minar";
var searchSplit = searchTerm.split(" ");
var availableSearches = [];
for(var i=0; i<values.length; i++) {
for(var j=0; j<searchSplit.length; j++) {
if(values[i].indexOf(searchSplit[j]) >= 0 &&
availableSearches.indexOf(values[i]) < 0){
availableSearches.push(values[i]);
}
}
}
console.log(availableSearches);
//["India taj mahal", "India Qutub Minar"]
This logic will search text with both India and Minar in the collection. If this is not exactly you are looking for, you can easily tweak logic.
Related Topics
How to Avoid Sending Input Fields Which Are Hidden by Display:None to a Server
Use HTML5 (Datalist) Autocomplete with 'Contains' Approach, Not Just 'Starts With'
How to Add Button Inside Input
A HTML Space Is Showing as %2520 Instead of %20
Svg Coordinates with Transform Matrix
What Are the CSS Properties That Get Elements Out of the Normal Flow
How to Use Use Text as the Background with CSS
Is the HTML Shown via 'View Source' Different from the HTML Shown in (Firebug) Developer Tools
Giving a Border to an HTML Table Row, <Tr>
How to Set an Image's Width and Height Without Stretching It
Including External HTML File to Another HTML File
Html5 Video Element Request Stay Pending Forever (On Chrome)
Fontawesome Icons Are Not Showing, Why
CSS Transition from Display None to Display Block, Navigation with Subnav