Question

I am using a datalist and need to detect when the user selects something from the drop-down list. A similar question has been asked BUT I need it so that the event fires ONLY when the user selects something from the datalist. If they type something in the input then I do NOT want the event to fire. (Notice in the accepted answer to the question I linked that they bind the input, which is not what I want). I've tried (with no success):

<datalist>
    <option>Option 1 Here</option> 
    <option>Option 2 Here</option>
</datalist>


$(document).on('change', 'datalist', function(){
   alert('hi');
});

EDIT: This question is different than the suggested question because it's a completely different question.

Was it helpful?

Solution

You can manually check it on change. But you need to check change of the input of datalist.

FIDDLE

$(document).on('change', 'input', function() {
  var options = $('datalist')[0].options;
  var val = $(this).val();
  for (var i = 0; i < options.length; i++) {
    if (options[i].value === val) {
      console.log(val);
      break;
    }
  }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

<input list="ff">
<datalist id="ff">
  <option>Option 1 Here</option>
  <option>Option 2 Here</option>
</datalist>

OTHER TIPS

In browser with the inputType property on the InputEvent you can use that to filter out any unwanted onInput events. This is "insertReplacementText" on Firefox 81 and null for Chrome/Edge 86. If you need to support IE11 you will need to validate the value is valid.

document.getElementById("browser")
  .addEventListener("input", function(event){
        if(event.inputType == "insertReplacementText" || event.inputType == null) {
          document.getElementById("output").textContent =  event.target.value;
          event.target.value = "";
    }
})
<label for="browser">Choose your browser from the list:</label>
<input list="browsers" name="browser" id="browser">
<datalist id="browsers">
  <option value="Edge">
  <option value="Firefox">
  <option value="Chrome">
  <option value="Opera">
  <option value="Safari">
</datalist>
<div id="output">
</div>

The solutions above all have a big problem. If the datalist has the option of (for example) "bob" and "bobby", as soon as someone types "bob", the code immediately says it's the same as clicking "bob"... but what if they were attempting to type "bobby"?

For a better solution, we need some more information. When listening for the 'input' event on the input field:

In Chromium-based browsers, when you type, delete, backspace, cut, paste, etc. in an input field, the event that is passed to the handler is an InputEvent, whereas when you select an option from the datalist, the event is just an Event type with a property of type that equals 'input'. (This is also true during an auto-fill, at least with BitWarden).

So you can listen for an 'input' event and check to see if it's an instance of InputEvent to determine if it's from autofill (which I think should be allowed since most of the time autofill won't be filling these types of fields, and if they do, it's usually a legit choice) / datalist selection.

In Firefox, it's different, though. It still provides an InputEvent, but it has an inputType property with the value of "insertReplacementText", which we can also use. Autofill does the same thing as Chromium browsers.

So here's a better solution:

$('#yourInput').on('input', function(){
    if (
        !(e instanceof InputEvent) ||
        e.inputType === 'insertReplacementText')
    ) {
        // determine if the value is in the datalist. If so, someone selected a value in the list!
    }
});

I wish the browsers had the same implementation that had an event type/property that was exclusive to datalist selection, but they don't so this is the best I've got. I haven't tested this on Safari (I don't have access or time right now) so you should probably take a look at it and see if it's the same or has other distinguishing differences.


UPDATE:

I noticed that if you already have the full word typed up (e.g. you typed in "Firefox") and then selected the option that matched what you had typed (e.g. you selected the "Firefox" option), it would not fire an "input" event, so you would not know that one of the options was chosen at that point. So, you'll also need a keydown/keypress/keyup event listener to listen to when they press enter. In Chromium browsers, though, it actually provides a key code of undefined when you hit enter or click on an option (yes, the click makes a keyup event). In Firefox, it says you hit Enter, but it doesn't fire any events I can find when you click an option.

Optimized Solution

$("input").on('input', function () {
    var inputValue = this.value;
    if($('datalist').find('option').filter(function(){
        return this.value == inputValue;        
    }).length) {
        //your code as per need
        alert(this.value);
    }
});

Please have look for your solution it's good to go. Have look for Demo

$(document).on('change', 'input', function(){
    var optionslist = $('datalist')[0].options;
    var value = $(this).val();
    for (var x=0;x<optionslist.length;x++){
       if (optionslist[x].value === value) {
          //Alert here value
          console.log(value);
          break;
       }
    }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input list="data">
<datalist id="data">
    <option value='1'>Option 1 Here</option> 
    <option value='2'>Option 2 Here</option>
</datalist>

More Optimize

$("input").on('input', function () {
  if($('datalist').find('option[value="'+this.value+'"]')){
    //your code as per need
    alert(this.value);
  }
});

This might only be Chrome, untested anywhere else!, but I see a change event triggered when an option is selected. Normally change only happens in textfields when the field loses focus. The change event triggers before the blur event IF it's a normal non-datalist change, so we have to check both: we're looking for a change that's not immediately followed by a blur:

var timer;
el.addEventListener('change', function(e) {
    timer = setTimeout(function() {
        el.dispatchEvent(new CustomEvent('datalistchange'));
    }, 1);
});
el.addEventListener('blur', function(e) {
    clearTimeout(timer);
});

And then you can listen for the custom datalistchange event like normally. Or you can just put your specific logic instead of the dispatchEvent.

jsFiddle here

jQuery('input').on('input', function () {
  if($('datalist[id="' + jQuery(this).attr('list') + '"]').find('option[value="'+this.value+'"]')){
    //your code as per need
    alert(this.value);
  }
});

so it will find the datalist associated with the input

8 years, and still no good answer

Simple space-suffix trick will do what you need, add &nbsp; at the end of each option, and then detect (and remove) it in "input" handler

<input list="my-options" id="my-input">
<datalist id="my-options">
  <option>Option 1 Here&nbsp;</option>
  <option>Option 2 Here&nbsp;</option>
</datalist>

<script>
$('#my-input').on('input',function (e)
{
        var v=$(e.target).val();
        if (v.slice(-1)=='\xa0')
        {
                $(e.target).val(v=v.slice(0,-1));
                console.log("option selected '"+v+"'");
        };
});
 
</script>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top