If you can turn the "value" associated with the option into a number, you can use unicode invisible characters, U+E0001
through U+E007F
to store the "value" part of the option alongside the title.
Append an arbitrary number of invisible characters to the end of your title. Since there are ~120 such invisible characters, each double-byte invisible character on the end of a title represents an integer from 0 to 120.
My solution below stores breaks up numbers into equal chunks, so that number parts stored in invisible characters need only be summed as is to get the original. If numbers could be broken up such that each invisible character represents increasingly larger portions of the original number, perhaps using powers of two or some other exponential math/magic, then the solution would be significantly faster.
const BASE_CODEPOINT = 'E0001';
const NUM_EMPTY_CHARS = 120; // actually 127, but let's be safe.
const numToEmptyChar = num => String.fromCodePoint(`0x${(parseInt(BASE_CODEPOINT, 16) + num).toString(16)}`);
const withNumberAsTrailingEmptySpace = (string, number, maxNumber = NUM_EMPTY_CHARS) => {
const numChars = Math.ceil(maxNumber / NUM_EMPTY_CHARS);
const numCharsMinusOne = Math.max(0, numChars - 1);
return string
+ numToEmptyChar(NUM_EMPTY_CHARS).repeat(numCharsMinusOne)
+ numToEmptyChar(number - (NUM_EMPTY_CHARS * numCharsMinusOne));
};
const numberFromTrailingEmptySpace = (string, maxNumber = NUM_EMPTY_CHARS) => {
const numChars = Math.ceil(maxNumber / NUM_EMPTY_CHARS);
let total = 0;
for (let i = 0; i < numChars; i++) {
const codePoint = string.codePointAt(string.length - 2 * (i + 1));
total += codePoint - parseInt(BASE_CODEPOINT, 16);
}
return total;
};
let mystring = "Anything at all can go here.....";
let maxNum = 2000;
let number = 1337;
let withNumber = withNumberAsTrailingEmptySpace(mystring, number, maxNum);
console.log(numberFromTrailingEmptySpace(withNumber, maxNum));
So, you'd end up creating your datalist via JS (untested):
const options = [
{label: "Foo", value: 1},
{label: "Bar", value: 2},
{label: "Baz", value: 3}
];
const input = document.createElement('input');
input.setAttribute('list', 'options');
input.addEventListener('change', e => {
// https://w3c.github.io/input-events/#interface-InputEvent-Attributes
const isDatalist = (typeof inputType === "undefined") || (inputType === "insertReplacementText");
if (isDatalist) {
const idx = numberFromTrailingEmptySpace(e.target.value, options.length);
const selectedOption = options[idx];
e.target.value = selectedOption.label;
console.log("The user selected option: ", selectedOption);
}
});
const datalist = document.createElement('datalist');
datalist.setAttribute('id', 'options');
options.forEach(({label}, idx) => {
const o = document.createElement('option');
o.label = withNumberAsTrailingEmptySpace(label, idx, options.length);
datalist.appendChild(o);
});
1
, should the text becomeFoo
? Should.value
return1
or3
if the user typesFoo
manually? – PlanetstruckFoo
,Bar
, etc should be accessible as suggestions - just like a traditional<select>
. If the user typesFoo
there will be two suggestions and the value should correspond with the selected value (when clicked or highlighted with keyboard). If no suggestions are selected and the text value isFoo
we can only assume the user selected the first option. – Prebend