How can I get the suggested values from an input datalist using pure Javascript?

149 views Asked by At

I'm using a plain text input plus datalist to suggest values as the user interacts with the input. I'd like to know if there's a way to get the suggested items shown.

E.g.

    document.getElementById('myBrowser').addEventListener('input', function() {
      console.log(this.list.suggested); // logs out the suggested values 
    });
    <label for="myBrowser">Choose a browser from this list:</label>
    <input list="browsers" id="myBrowser" name="myBrowser" />
    <datalist id="browsers">
      <option value="Chrome"></option>
      <option value="Firefox"></option>
      <option value="Opera"></option>
      <option value="Safari"></option>
      <option value="Microsoft Edge"></option>
    </datalist>

So if I type 'Fire' the suggestion values should just be ['Firefox']

2

There are 2 answers

20
Adri On BEST ANSWER

You can do something like this :

// Obtain the available browsers
let options = Array.from(document.querySelectorAll('#browsers option')).map((option) => option.value);

document.getElementById('myBrowser').addEventListener('input', function () {
  const hint = this.value.toLowerCase();
  // Obtain options matching input
  const suggestions = options.filter((option) => option.toLowerCase().includes(hint));

  console.log(suggestions);
});
<label for="myBrowser">Choose a browser from this list:</label>
<input list="browsers" id="myBrowser" name="myBrowser" />
<datalist id="browsers">
  <option value="Chrome"></option>
  <option value="Firefox"></option>
  <option value="Opera"></option>
  <option value="Safari"></option>
  <option value="Microsoft Edge"></option>
</datalist>

This obtains the exact same list as on Chrome, but that may not be the case on other browsers.

As for directly accessing the proposed list from the browser, I cannot say for sure if that is doable.

0
Peter Seliger On

One could implement such an additional suggestedValue property as getter of the HTMLInputElement.prototype.

The suggested solution has to be implemented as direct property of an input-element and not like the OP suggested ... "[inputElement].list.suggested" ... as property of a datalist-element ... since <datalist/>, via its id-attribute, can be referred to by more than just one element, each via its list-attribute.

Thus, a basic possible implementation (a bullet-proof version has to support multiple word matches from both, option-values and option-labels alike) and usage could look like follows ...

document
  .querySelectorAll('input[list]')
  .forEach(elmInput =>
    elmInput.addEventListener('input', ({ currentTarget }) =>
      console.log({
        currentTarget,
        suggestedValues: currentTarget.suggestedValues,
      })
    )
  );
body { margin: 0; }
.as-console-wrapper { min-height: calc(100% - 50px); }
<div>
  <input placeholder="no browser" />
  <input list="" placeholder="broken browser" />
</div>
<div>
  <input list="browser-list" placeholder="my browser" />
  <input list="browser-list" placeholder="your browser" />
</div>

<datalist id="browser-list">
  <option value="Chrome"></option>
  <option value="Firefox"></option>
  <option value="Opera"></option>
  <option value="Safari"></option>
  <option value="Microsoft Edge"></option>
</datalist>

<script>
Object.defineProperty(HTMLInputElement.prototype, 'suggestedValues', {
  get() {
    const search = this.value.trim().replace(/\s+/, ' ').toLowerCase();
    const { list } = this;

    return (list ?? null) && [
      ...list.options
    ]
    .map(({ value }) => value)
    .filter(value =>
      value.trim().replace(/\s+/, ' ').toLowerCase().includes(search)
    );
  },
  enumerable: true,
  configurable: true,
});
</script>