Search code examples
javascripthtmlhtml-datalist

Datalist doesn't disappear when input is blurred


I have an input with a datalist. Part of the behavior involves the input being blurred under certain conditions. When I call blur() however, the datalist doesn't go away like I expect and, even worse, clicking elsewhere on the screen doesn't cause it to go away either; I am forced to either click a different input on screen (not just any other element) or click back into that input and then click away to get it to go away. Am I missing something or is this just unavoidable?

The following is about as minimal as an example can be:

document.getElementById('testinput').addEventListener('input', e => e.target.blur())
<input id="testinput" type="text" list="testlist" />
<datalist id="testlist">
  <option>TestOption</option>
</datalist>

Other than unlinking the datalist from the input when I want to blur it, so that it no longer applies, is there anything else I can do to make this work? It seems like very basic behavior.

edit: unlinking alone isn't enough, since the autocomplete suggestions still show up and have the same behavior. You also need autocomplete="off" in addition to unlinking the datalist to get the expected behavior. Seems like a lot of hassle for what should be default behavior.


Solution

  • To be honest, this is not the best solution, but you can try deleting and returning the list attribute as needed. Example below:

    const element = document.getElementById('testinput');
    
    element.addEventListener('input', e => {
      e.target.blur()
    });
    
    element.addEventListener('focusin', e => {
      e.target.setAttribute('list', 'testlist'); 
    })
    
    element.addEventListener('focusout', e => {
      e.target.removeAttribute('list')
    });
    <input id="testinput" type="text" list="testlist" />
    <datalist id="testlist">
      <option>TestOption</option>
    </datalist>

    Or a more compact version, without the focusin and focusout events:

    const element = document.getElementById('testinput');
    
    element.addEventListener('input', e => {
      e.target.removeAttribute('list');
      e.target.blur();
      setTimeout(() => {
        e.target.setAttribute('list', 'testlist');
      });
    });
    <input id="testinput" type="text" list="testlist" />
    <datalist id="testlist">
      <option>TestOption</option>
    </datalist>

    With autocomplete you can try to solve in a similar way (toggling "on" | "off"):

    const element = document.getElementById('testinput');
    
    element.addEventListener('input', e => {
      e.target.removeAttribute('list');
      e.target.setAttribute('autocomplete', 'off');
      e.target.blur();
      setTimeout(() => {
        e.target.setAttribute('list', 'testlist');
        e.target.setAttribute('autocomplete', 'on');
      });
    });
    <input id="testinput" type="text" list="testlist" autocomplete="on" />
    <datalist id="testlist">
      <option>TestOption</option>
    </datalist>