Perform action when clicking HTML5 datalist option
Asked Answered
C

11

51

I'm using a <datalist>

<datalist id="items"></datalist>

And using AJAX to populate the list

 function callServer (input) {
    xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function(){
        if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
            //return the JSON object
            console.log(xmlhttp.responseText);
            var arr = JSON.parse(xmlhttp.responseText);
            var parentDiv = document.getElementById('items');
            parentDiv.innerHTML = "";
            //fill the options in the document
            for(var x = 0; x < arr.length; x++) {
                var option = document.createElement('option');
                option.value = arr[x][0];
                option.innerHTML = arr[x][1];
                //add each autocomplete option to the 'list'
                option.addEventListener("click", function() {
                  console.log("Test");
                });
                parentDiv.appendChild(option);
            };

        }
    }
    xmlhttp.open("GET", "incl/search.php?value="+input.value, true);
    xmlhttp.send();
}

However I can't get it to perform an action when I click on a selection in the datalist, for example if I type in "Ref F" and the item "Ref flowers" comes up, if I click on it I need to execute an event.

How can I do this?

option.addEventListener("click", function() {
option.addEventListener("onclick", function() {
option.addEventListener("change", function() {
Cindy answered 4/5, 2015 at 4:39 Comment(2)
Pls provide js fiddle of the same..I think you are attaching events at the wrong time..You need to bind events after elements are appended in the DOM..Wouldst
You can use input event with setTimeout to avoid triggering input each time: https://mcmap.net/q/354667/-trigger-event-on-selecting-item-from-datalist-not-when-typingTotal
K
63

Sorry for digging up this question, but I've had a similar problem and have a solution, that should work for you, too.

function onInput() {
    var val = document.getElementById("input").value;
    var opts = document.getElementById('dlist').children;
    for (var i = 0; i < opts.length; i++) {
      if (opts[i].value === val) {
        // An item was selected from the list!
        // yourCallbackHere()
        alert(opts[i].value);
        break;
      }
    }
  }
<input type='text' oninput='onInput()' id='input' list='dlist' />

<datalist id='dlist'>
  <option value='Value1'>Text1</option>
  <option value='Value2'>Text2</option>
</datalist>

This solution is derived from Stephan Mullers solution. It should work with a dynamically populated datalist as well.

Unfortunaltely there is no way to tell whether the user clicked on an item from the datalist or selected it by pressing the tab-key or typed the whole string by hand.

Kappa answered 25/8, 2015 at 13:22 Comment(4)
you must also distinguish between that and a paste event..very different paths which might not conclude to the same end pointCancellation
On IE11, this works exactly as in Firefox. What Version of IE are you using and what exactly does not work?Kappa
For me, using document.getElementById('dlist').childNodes did not give the expected results, but switching it to document.getElementById('dlist').children works perfectly.Actinal
It is probably a good idea to use children instead of childNodes, since childNodes also returns comment nodes, I edited my answer accordingly. Thanks for the info.Kappa
S
23

Due to the lack of events available for <datalist> elements, there is no way to a selection from the suggestions other than watching the input's events (change, input, etc). Also see my answer here: Determine if an element was selected from HTML 5 datalist by pressing enter key

To check if a selection was picked from the list, you should compare each change to the available options. This means the event will also fire when a user enters an exact value manually, there is no way to stop this.

document.querySelector('input[list="items"]').addEventListener('input', onInput);

function onInput(e) {
   var input = e.target,
       val = input.value;
       list = input.getAttribute('list'),
       options = document.getElementById(list).childNodes;

  for(var i = 0; i < options.length; i++) {
    if(options[i].innerText === val) {
      // An item was selected from the list!
      // yourCallbackHere()
      alert('item selected: ' + val);
      break;
    }
  }
}
<input list="items" type="text" />
<datalist id="items">
  <option>item 1</option>
  <option>item 2</option>
</datalist>
Shoeblack answered 5/5, 2015 at 13:40 Comment(2)
Thanks for the snipper, however this is not working on my solution, I assume because the datalist is being constantly repopulated and emptied.Cindy
Works perfectly, if you have the options set with <option value="item 1"> then you'll need to use options[i].value instead of innerTextSweven
B
21

Use keydown

Contrary to the other answers, it is possible to detect whether an option was typed or selected from the list.

Both typing and <datalist> clicks trigger the input's keydown listener, but only keyboard events have a key property. So if a keydown is triggered having no key property, you know it was a click from the list.

Demo:

const dinput = document.getElementById('dinput');
let eventSource = '';

dinput.addEventListener('keydown', (e) => {
  eventSource = e.key ? 'typed' : 'clicked';
});
dinput.addEventListener('input', (e) => {
  if (eventSource === 'clicked') {
    alert('CLICKED! ' + e.target.value);
  }
});
<input type="text" id="dinput" list="dlist" />

<datalist id="dlist">
  <option value="Value1">Text1</option>
  <option value="Value2">Text2</option>
</datalist>

Notice it doesn't alert if the value being clicked is already in the box, but that's probably desirable. (This could also be added by using an extra tracking variable that will be toggled in the keydown listener.)

Blizzard answered 30/11, 2020 at 12:26 Comment(4)
Excellent! This is very clever. I was trying to hook up both mouse and key events to the input, only for it to, quite weirdly, raise key event despite clicking on the option in the dropdown. This seems to be the only way to distinguish b/w the two.Crummy
Great solution. Unfortunately Safari (Version 15.1) does not trigger an keydown event when selecting an option of the datalist, so eventSource will not get updates and remain in the last state.Subtemperate
This answer's snippet works for me as described in Chrome (Mac), but not in Firefox (Mac). (In the latter, the alert never appears.)Sahaptin
It doesn't work on Firefox on Windows too. I guess only Chromium-based browsers will work here. That's unfortunateSeaport
R
6

Datalist actually don't have an event (not all browsers), but you can detect if a datalist option is selected in this way:

<input type="text" list="datalist" />

<datalist id="datalist">
  <option value="item 1" />
  <option value="item 2" />
</datalist>

window.addEventListener('input', function (e) {
 let event = e.inputType ? 'input' : 'option selected'
 console.log(event);
}, false);

demo

Regicide answered 6/4, 2022 at 3:34 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewEscritoire
V
4

Shob's answer is the only one which can detect when an option gets clicked as well as not trigger if an intermediary written text matches an option (e.g.: if someone types "Text1" to see the options "Text11", "Text12", etc. it would not trigger even if "Text1" is inside the datalist).

The original answer however did not seem to work on newer versions of Firefox as the keydown event does not trigger on clicks so I adapted it.

let keypress = false;
document.getElementById("dinput").addEventListener("keydown", (e) => {
    if(e.key) {
        keypress = true;
    }
});
document.getElementById("dinput").addEventListener('input', (e) => {
    let value = e.target.value;
    if (keypress === false) {
        // Clicked on option!
        console.debug("Value: " + value);
    }
    keypress = false;
});
<input type="text" id="dinput" list="dlist" />

<datalist id="dlist">
  <option value="Value1">Text1</option>
  <option value="Value2">Text2</option>
</datalist>
Vries answered 24/2, 2021 at 13:29 Comment(0)
H
2

Datalist don't support click listener and OnInput is very costly, checking everytime all the list if anything change.

What I did was using:

document.querySelector('#inputName').addEventListener("focusout", onInput);

FocusOut will be triggered everytime a client click the input text and than click anywhere else. If they clicked the text, than clicked somewhere else I assume they put the value they wanted.

To check if the value is valid you do the same as the input:

   function onInput(e) {
        var val = document.querySelector('#inputName').value;
        options = document.getElementById('datalist').childNodes;
        for(var i = 0; i < options.length; i++) {
             if(options[i].innerText === val) {
                  console.log(val);
                  break;
             }
        }
    }
Hebe answered 3/3, 2018 at 18:7 Comment(0)
F
1
<input type="text" id="buscar" list="lalista"/>
<datalist id="lalista">
  <option value="valor1">texto1</option>
  <option value="valor2">texto2</option>
  <option value="valor3">texto3</option>  
</datalist>
//0 if event raised from datalist; 1 from keyboard
let idTimeFuekey = 0;  
buscar.oninput = function(){
    if(buscar.value && idTimeFuekey==0) {
        alert('Chévere! vino desde la lista')
    }
};
buscar.onkeydown = function(event){
    if(event.key){ //<-- for modern & non IE browser, more direct solution
        window.clearInterval(idTimeFuekey);
        idTimeFuekey = window.setInterval(function(){ //onkeydown --> idTimeFuekey++ (non 0)
            window.clearInterval(idTimeFuekey);
            idTimeFuekey = 0 //after 500ms onkeydown --> 0 (could work 500, 50, .. 1)
        }, 500) 
    }
}
Firebreak answered 15/12, 2021 at 3:15 Comment(2)
para Internet ExplorerFirebreak
Maybe try to explain your code there.Triangulation
E
1

After having this problem and not finding a suitable solution, I gave it a shot.

What I did was look at the "inputType" of the given input event on top of the event toggle variable from above, like so:

eventSource = false;

const selector = document.getElementById("yourElementID");

selector.addEventListener('input', function(evt) {
    if(!eventSource) {
        if(evt.inputType === "insertReplacementText") {
            console.log(selector.value);
        }
    }
});

selector.addEventListener('keydown', function(evt) {
    eventSource = !evt.key;
});

This works if you want to allow the user to search a field but only hit a specific function/event on selection from the datalist itself. Hope it helps!

Edit: Forgot to mention this was done through Firefox and has not been tested on other browsers.

Encircle answered 22/2, 2023 at 18:26 Comment(0)
J
1

DATALIST CLICK EVENT

UPDATED SOLUTION: Uses Event inputType

There are no ‘click’ listeners for the element. The following solution leverages the inputType event native to the event listener for ‘input’ on the element, which is in my opinion as clean as it gets. Unfortunately, not all the vendors follow the same method that Firefox does. If they did, I would not have to use the navigator.userAgent to try to figure out what browser is being used in order to “know” what method to follow. But the following example can be expanded upon for the rest of the browsers out there.

  <body>
    <form>
      <label for="customer_name">Name</label>
      <input id="customer_name" list="customer_list" type="text" data-list="customer" />
      <datalist id="customer_list">
        <option value="Chrome"></option>
        <option value="Edge"></option>
        <option value="Firefox"></option>
        <option value="Opera"></option>
        <option value="Safari"></option>
      </datalist>
    </form>
  </body>
  <script>
    const DATA_LIST = document.querySelectorAll('[data-list]')
    DATA_LIST.forEach((element) => {
      element.addEventListener('input', (event) => {
        const browser = navigator.userAgent
        /////  FIREFOX on WINDOWS  /////
        if (browser.includes('Firefox')) {
          //  KEYBOARD EVENT  //
          if (event.inputType != 'insertReplacementText') {
            return
          }
          //  MOUSE CLICK  //
          if (event.inputType === 'insertReplacementText') {
            alert(`Mouse clicked '${event.target.value}' in the datalist...\nusing Firefox browser for Windows`)
          }
        }
        /////  CHROME & Edge on WINDOWS  /////
        if (browser.includes('Chrome')) {
          //  KEYBOARD EVENT  //
          if (event.inputType) {
            return
          }
          //  MOUSE CLICK  //
          if (!event.inputType) {
            alert(`Mouse clicked '${event.target.value}' in the datalist...\nusing a Chromium browser for Windows`)
          }
        }
      })
    })
  </script>
Judicature answered 13/10, 2023 at 22:57 Comment(2)
This will work for Chrome, Edge and Firefox on Windows. You could add more logic like browser.includes('Macintosh') for Safari on Mac. But, the logic to handle the event may need to be tweaked since I didn't test on Mac. Then you'd have to do the same for iOS and Android etc. But following this method it is possible. Other than that if all vendors follow the same as Firefox this would literally be like 6 lines of code. Oh well!Chitwood
Excellent. I've been looking at the top of the page for ages, discovered inputType all by myself and then scrolled down!Chromatology
P
0

For React, if you debug in Chrome, you will notice the nativeEvent type will be different:

  • keyboard triggered is InputEvent
  • option selection triggered is Event

Hope this can help

Parodist answered 18/2 at 12:16 Comment(0)
W
-2

Well, at least in Firefox the onselect event works on the input tag

<input type="text" id="dinput" list="dlist" onselect="alert(this.value)"/>

<datalist id="dlist">
  <option value="Value1">Text1</option>
  <option value="Value2">Text2</option>
</datalist>
Weinrich answered 13/7, 2021 at 1:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.