(I can't find it, but then again I don't really know how to search for it.)
I want to use <input list=xxx>
and <datalist id=xxx>
to get autocompletion, BUT I want the browser to match all options by 'contains' approach, instead of 'starts with', which seems to be standard. Is there a way?
If not simply, is there a way to force-show suggestions that I want to show, not those that the browser matched? Let's say I'm typing "foo" and I want to show options "bar" and "baz". Can I force those upon the user? If I just fill the datalist with those (with JS), the browser will still do its 'starts with' check, and filter them out.
I want ultimate control over HOW the datalist options show. NOT over its UI, flexibility, accessibility etc, so I don't want to completely remake it. Don't even suggest a jQuery plugin.
If I can ultimate-control form element validation, why not autocompletion, right?
edit: I see now that Firefox does use the 'contains' approach... That's not even a standard?? Any way to force this? Could I change Firefox's way?
edit: I made this to illustrate what I'd like: http://jsfiddle.net/rudiedirkx/r3jbfpxw/
- HTMLWG's specs on
[list]
- W3's specs on
datalist
- DavidWalsh example
- HONGKIAT's summary on behaviors..?
'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>
Edit
A different approach of displaying the search content, to make clear, what happens. This works in Chrome as well. Inspired by Show datalist labels but submit the actual value
'use strict';
var datalist = {
r: ['ralph', 'ronny', 'rudie'],
ru: ['rudie', 'rutte', 'rudiedirkx'],
rud: ['rudie', 'rudiedirkx'],
rudi: ['rudie'],
rudo: ['rudolf'],
foo: [
{ value: 42, text: 'The answer' },
{ value: 1337, text: 'Elite' },
{ value: 69, text: 'Dirty' },
{ value: 3.14, text: 'Pi' }
]
},
SEPARATOR = ' > ';
function updateList(that) {
var lastValue = that.lastValue,
value = that.value,
array,
key,
pos = value.indexOf('|'),
start = that.selectionStart,
end = that.selectionEnd;
if (lastValue !== value) {
if (value !== '') {
if (value in datalist) {
key = value;
} else {
Object.keys(datalist).some(function (a) {
return ~a.toLowerCase().indexOf(value.toLowerCase()) && (key = a);
});
}
}
that.list.innerHTML = key ? datalist[key].map(function (a) {
return '<option data-value="' + (a.value || a) + '">' + value + (value === key ? '' : SEPARATOR + key) + SEPARATOR + (a.text || a) + '</option>';
}).join() : '';
updateInput(that);
that.lastValue = value;
}
}
function updateInput(that) {
var value = that.value,
pos = value.lastIndexOf(SEPARATOR),
start = that.selectionStart,
end = that.selectionEnd;
if (~pos) {
value = value.slice(pos + SEPARATOR.length);
}
Object.keys(that.list.options).some(function (option) {
var o = that.list.options[option],
p = o.text.lastIndexOf(SEPARATOR);
if (o.text.slice(p + SEPARATOR.length) === value) {
value = o.getAttribute('data-value');
return true;
}
});
that.value = value;
that.setSelectionRange(start, end);
}
document.getElementsByTagName('input').xx.addEventListener('keyup', function (e) {
updateList(this);
});
document.getElementsByTagName('input').xx.addEventListener('input', function (e) {
updateInput(this);
});
<input list="xxx" name="xx" id="xx">
<datalist id="xxx" type="text"></datalist>
yet this thread is posted about 2 years ago. but if you are reading this thread, you maybe need to check a newer version of your browser:
Current specification: https://html.spec.whatwg.org/multipage/forms.html#the-list-attribute
User agents are encouraged to filter the suggestions represented by
the suggestions source element when the number of suggestions is
large, including only the most relevant ones (e.g. based on the user's
input so far). No precise threshold is defined, but capping the list
at four to seven values is reasonable. If filtering based on the
user's input, user agents should use substring matching against both
the suggestions' label and value.
And when this post written, behavior of Firefox (51) and Chrome (56) had already been changed to match the specification.
which means what op want should just work now.
this fiddle here has cracked what you are asking for
But I am not sure how to make it work without this dependency as the UI looks bit odd and out of place when used along with Bootstrap.
elem.autocomplete({
source: list.children().map(function() {
return $(this).text();
}).get()