Show datalist labels but submit the actual value

Stephan Muller picture Stephan Muller · Apr 26, 2015 · Viewed 97.8k times · Source

Currently the HTML5 <datalist> element is supported in most major browsers (except Safari) and seems like an interesting way to add suggestions to an input.

However, there seem to be some discrepancies between the implementation of the value attribute and the inner text on the <option>. For example:

<input list="answers" name="answer">
<datalist id="answers">
  <option value="42">The answer</option>
</datalist>

This is handled differently by different browsers:

Chrome and Opera:
Datalist in Chrome/Opera

FireFox and IE 11:
Datalist in FireFox

After selecting one, the input is filled with the value and not the inner text. I only want the user to see the text ("The answer") in the dropdown and in the input, but pass the value 42 on submit, like a select would.

How can I make all browsers have the dropdown list show the labels (inner text) of the <option>s, but send the value attribute when the form is submitted?

Answer

Stephan Muller picture Stephan Muller · Apr 26, 2015

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 inputs on the same page with one or more datalists 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