How can i validate the input from a html5 Datalist?
Asked Answered
S

5

11

I would like to know how I can validate the input value that comes from a Datalist. I mean, if I have a Datalist where the user can start to write a value and then choosing it from the Datalist, but the user decides to don't choose any value from the list and he submits the form with the incomplete value, the sent value will be wrong.

I thought about iterate over all the elements of the Datalist but I think that it can't be a good idea if the Datalist has more than 1.000 values and I don't know any other way to validate it.

Here is an example of the Datalist that I'm going to use:

<input type="text" list="colours">

<datalist id="colours">
    <option value="Red" data-id="1">
    <option value="Blue" data-id="2">
    <option value="Green" data-id="3">
    <option value="Black" data-id="4">
    <option value="White" data-id="5">
</datalist>
Scold answered 24/7, 2014 at 13:10 Comment(0)
T
17

Try this:

<input type="text" list="colours" id='txt'>

And on form submit you can check:

var val = $("#txt").val();

var obj = $("#colours").find("option[value='" + val + "']");

if(obj != null && obj.length > 0)
    alert("valid");  // allow form submission
else
    alert("invalid"); // don't allow form submission
Threnode answered 24/7, 2014 at 14:41 Comment(0)
S
13

You can do this with HTML5 validation using pattern. It's easier if you're populating your datalist with some sort of template, but it would look something like this (Note that you would need additional code to handle the validation - I just added very simple CSS to display the validation state)

It's worth noting that this automatically blocks form submission and provides proper semantics for accessibility and other standards-compliant interoperability.

input:valid {
  border: 1px solid green;
}
input:invalid {
  border: 1px solid red;
}
<input type="text" list="colours"
pattern="^(Red|Blue|Green|Black|White)$"
>

<datalist id="colours">
    <option value="Red" data-id="1">
    <option value="Blue" data-id="2">
    <option value="Green" data-id="3">
    <option value="Black" data-id="4">
    <option value="White" data-id="5">
</datalist>
Sainthood answered 4/11, 2020 at 18:44 Comment(0)
S
8

I'd like to share a non-jquery alternative, only in Js:

function is_valid_datalist_value(idDataList, inputValue) {
  var option = document.querySelector("#" + idDataList + " option[value='" + inputValue + "']");
  if (option != null) {
    return option.value.length > 0;
  }
  return false;
}

function doValidate() {
  if (is_valid_datalist_value('colours', document.getElementById('color').value)) {
    alert("Valid");
  } else {
    alert("Invalid");
  }
}
<form onsubmit="return false">
  <input type="text" id="color" list="colours">
  <datalist id="colours">
    <option value="Red" data-id="1" />
    <option value="Blue" data-id="2" />
    <option value="Green" data-id="3" />
    <option value="Black" data-id="4" />
    <option value="White" data-id="5" />
    </datalist>
  <button onclick="doValidate();">Send</button>
</form>
Salami answered 1/3, 2019 at 17:38 Comment(0)
V
3

If you use jQuery find method, it will traverse the DOM tree trying to find the correct element taking into account the value of one of its attributes, and looking at your comment, I think that you are concerned about performance.

Your first idea about iterate over all the options and check the value property is better (speaking about performance) than traversing the DOM tree looking for an element with a particular value in one of their attributes (look at this comparison). You need to be aware that shorter code is not the same as faster code.

A faster solution is to generate an array of strings at the beginning and search the correct value inside it in the validation process:

//---At the beginning of your application
let list = Array.prototype.map.call(document.getElementById("colours").options, (option) => option.value);

//---Later in your validation process
if (list.indexOf(value) < 0) {
    //Invalid
}

Another faster solution is to generate an object at the beginning and use it as a hash map, checking for the correct value inside it in the validation process:

//---At the beginning of your application
let hashmap = Array.prototype.reduce.call(document.getElementById("colours").options, (obj, option) => {
    if (!obj[option.value]) { obj[option.value] = true; }
    return obj;
}, {});

//---Later in your validation process
if (!hashmap[value]) {
    //Invalid
}

Here you have the four methods compared in measurethat:

1 - Your first idea (iterate over all the Datalist options)

2 - Use the jQuery find method (@nsthethunderbolt solution)

3 - Create an array of strings at the beginning and search the value in the Array in the validation process

4 - Create a hash map at the beginning and check if the value is true in the validation process

https://www.measurethat.net/Benchmarks/Show/4430/0/search-an-option-inside-a-datalist

Velazquez answered 3/3, 2018 at 13:20 Comment(0)
F
1

Just to clarify, I show you a working example, which initialize an array of options:

const colours = [...document.querySelectorAll('#colours option')].map( option => option.value)

document.getElementById("colour").addEventListener("keyup", e => {
  if(colours.includes(e.target.value)) {
    document.querySelector('#colour').classList.remove('invalid')
    document.querySelector('#colour').classList.add('valid')
  }
  else {
    document.querySelector('#colour').classList.remove('valid')
    document.querySelector('#colour').classList.add('invalid')
  }
})
.valid {
  border: 3px solid green;
}
.invalid {
  border: 3px solid red;
}
<input id="colour" type="text" list="colours">

<datalist id="colours">
    <option value="Red" data-id="1">
    <option value="Blue" data-id="2">
    <option value="Green" data-id="3">
    <option value="Black" data-id="4">
    <option value="White" data-id="5">
</datalist>
Fowler answered 7/1, 2022 at 23:52 Comment(1)
a little issue is that if I use copy-and-paste a valid value, like Red, using mouse... I get red - invalid. Maybe using oninput listener can be solved.Salami

© 2022 - 2024 — McMap. All rights reserved.