Search code examples
javascripthtmljqueryformsautosuggest

Deferred loading of input datalist in Firefox


I would like to create an input field for a username where it should be possible to choose from a large number of names / usernames. It should be possible to enter a partial string contained in the name or username. I would like to not load the list initially but after the user has typed a minimum of a number of characters (e.g. 3) because the list is ultimately very large and should be reduced to a subset by the initial 3 characters.

enter image description here

This works perfectly if using <input> with <datalist> in Chrome or Chromium, but does not work as expected in Firefox. I have not tested other browsers yet.

The expected behaviour (and the way it works in Chromium) is I type my characters and after the third character the drop-down autosuggest list is loaded and automatically displayed. If I continue typing, the list continues to get filtered. Additionally, I can make the list disappear with escape and reappear with arrow down or arrow up.

In Firefox, the list does not automatically appear. I have to type another character and then backspace. Also, I cannot make it appear with arrow down or arrow up. (The solution does work in Firefox though, if the list is created initially and not lazy-loaded, but this is not an option as described above).

What I would like to find is a way to make the suggest list automatically open (or on key press) or solve this differently (without datalist).

Requirements

  • Use as little external libraries as possible. I currently use jquery, it would be ok if it is a jquery solution, but pure JavaScript would be preferable.
  • Automatically having the suggest-list drop down (once the list is loaded) in all browsers would be ideal. But at least have a possibility to drop-down the list via keypress or button.

Examples

Initially populated datalist

  • This also works in Firefox.
  • The list should appear as soon as you type one of the characters contained in the list (e.g. '1', 'o', '12' etc.)

<input class="my-input-class" placeholder="Enter '123' ... or 'one' ... " list="myDatalist" size="100">
<datalist id="myDatalist">
  <option value="1">one</option>
  <option value="12">one two</option>
  <option value="123">one two three</option>
  <option value="1234">one two three four</option>
  <option value="12345">one two three four five</option>
  <option value="123456">one two three four five six</option>
</datalist>

Lazy populated datalist

  • does not work with Firefox
  • The list should appear as soon as you type at least three of the characters contained in the list (e.g. '123' or 'one')

var previous = null;

function delayedCallback(callback, ms) {
  var timer = 0;

  return function() {
    var context = this,
      args = arguments;
    clearTimeout(timer);
    timer = setTimeout(function() {
      callback.apply(context, args);
    }, ms || 0);
  };
}

function generateSelectList(inputElement) {
  var value = inputElement.val();

  var optionList = {
    '123': 'one two three',
    '1234': 'one two three four',
    '12345': 'one two three four five',
    '123456': 'one two three four five six',
    '1234567': 'one two three four five six seven',
  }

  $('#myDatalist').remove();

  dl = document.createElement('datalist');
  inputElement.attr('list', 'myDatalist');
  dl.id = 'myDatalist';

  for (var key in optionList) {
    var option = document.createElement('option');
    option.value = key;
    option.append(optionList[key]);
    dl.append(option);
  }
  inputElement.append(dl);
}

$(document).on('input', '.my-input-class', delayedCallback(function(event) {
  var inputElement = $(event.target);
  var value = inputElement.val();

  // only get result list if
  // - minimum 3 chars
  // - same result list has not already been fetched (if string starts with previous string)
  if (value.length >= 3) {
    if (previous && value.startsWith(previous) && $('#myDatalist').length) {
      console.log("DO NOT fetch list (again) value=" + value + " previous=" + previous);
    } else {
      console.log("fetch list: value=" + value);
      generateSelectList(inputElement);
      previous = value;
    }
  }

}, 500));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<input class="my-input-class" placeholder="Enter '123' ... or 'one' ... (at least 3 characters)" list="myDatalist" size="100">


I searched for some solutions or if others had the same problem. I did find some complaints about datalist not working as expected or not working the same in different browsers and also suggestions to use some jquery libraries. While I could just use one of the suggested solutions, I am not sure if they will solve my problem or if there is a way to make this work with datalist and other browsers (besides Chrome).


Solution

  • As per canIuse there is a bug in Firefox which requires autocomplete to be set to off for datalist to work properly. Maybe this would help?