HTML multiple selections with datalist

 
 
  • Gérald Barré
 

The multiple attribute (specification) on an <input> element indicates that multiple values can be selected. However, browsers do not support this attribute when a <datalist> element is used. Only one suggestion is shown at a time.

Let's enhance the native <input> to support datalists with multiple values! Here's the final result:

Source code of the demo: demo.zip
HTML
<input type="text" list="Suggestions" multiple />

<datalist id="Suggestions">
  <option>option 1</option>
  <option>option 2</option>
  <option>option 3</option>
</datalist>

The approach dynamically updates the option elements of the datalist as you type, removing already-selected values and prefixing the remaining options with the current selections. For instance, if the input value is option 2,, the HTML will be:

HTML
<input type="text" list="Suggestions" multiple value="option 2," />

<datalist id="Suggestions">
  <option>option 2, option 1</option>
  <option>option 2, option 3</option>
</datalist>

Here's the TypeScript code to dynamically update the datalist's children:

TypeScript
document.addEventListener("DOMContentLoaded", function () {
    const separator = ',';

    for (const input of document.getElementsByTagName("input")) {
        if (!input.multiple) {
            continue;
        }

        if (input.list instanceof HTMLDataListElement) {
            const optionsValues = Array.from(input.list.options).map(opt => opt.value);
            let valueCount = input.value.split(separator).length;

            input.addEventListener("input", () => {
                const currentValueCount = input.value.split(separator).length;

                // Do not update list if the user doesn't add/remove a separator
                // Current value: "a, b, c"; New value: "a, b, cd" => Do not change the list
                // Current value: "a, b, c"; New value: "a, b, c," => Update the list
                // Current value: "a, b, c"; New value: "a, b" => Update the list
                if (valueCount !== currentValueCount) {
                    const lsIndex = input.value.lastIndexOf(separator);
                    const str = lsIndex !== -1 ? input.value.substr(0, lsIndex) + separator : "";
                    filldatalist(input, optionsValues, str);
                    valueCount = currentValueCount;
                }
            });
        }
    }

    function filldatalist(input: HTMLInputElement, optionValues: string[], optionPrefix: string) {
        const list = input.list;
        if (list && optionValues.length > 0) {
            list.innerHTML = "";

            const usedOptions = optionPrefix.split(separator).map(value => value.trim());

            for (const optionsValue of optionValues) {
                if (usedOptions.indexOf(optionsValue) < 0) { // Skip used values
                    const option = document.createElement("option");
                    option.value = optionPrefix + optionsValue;
                    list.append(option);
                }
            }
        }
    }
});

Adding this script to your page will detect inputs with the multiple attribute and update their behavior to support the datalist.

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?