A few months ago, I stopped writing AJAX-style auto-complete inputs on my web apps. Instead, I started using the HTML5 DataList feature. It’s a pretty simple, and fairly slick way to do auto-complete on an input text box (as long as your list isn’t too long, since all items will be rendered into the HTML of the page).
But I found myself running into a problem: how do I get a numeric or other ID out of the selected option, when I am showing a human-friendly code or description as the value?
The DataList has a collection of Option tags with a value, and you can provide a description (though I don’t know if this is part of the standard or not). When you attach a DataList to an Input, you end up with a nice drop-list of items that can be selected, or auto-filled as you type:
But the vast majority of the time, I need to get the id of the item that was selected – not the human-friendly name/code or description.
Unfortunately, the DataList spec does not include any kind of “id” attribute for an Option. Sure, you could use an HTML “id” – but this would turn the item into a 1st class HTML element with a CSS-selectable id.
That’s not the kind of id I want, here.
So… what’s the answer? How do I store and retrieve the object id of the option?
Data-* Attributes To The Half-Rescue
HTML supports the notion of data-* attributes for any given HTML element. You can name and apply any attribute you want, and have it contain any data you want, as long as the attribute starts with “data-“.
Given that, I added a “data-id” to my DataList options, and included my object id in there.
But now I’m running into the problem of retrieving the data-id from the selected value because there is no “selected” or “checked” or other marker on a DataList to tell you which item from the list was selected. Instead, you only get the value that was specified for the option.
To get the data-id out of the option, some slightly clever jQuery can be employed.
Get The Data-Id By The Option Value
Assuming you have a unique list of option values, you can find the selected option by using a bit of jQuery. Once you have the selected option, you can use the .data() method to retrieve the data-attribute in question:
If you run this code on the “input” event of the input box, you’ll get the data-id of the selected option as soon as an option is selected – whether it is selected by mouse, arrow keys on the keyboard, or by typing in the complete item value.
Note that you do need to account for a selected option not being found, because each time you change the selected value, the “input” event will trigger. This means typing 8 characters will run the jQuery code 8 times – but it will only find the exact matching option when typing.
If you want to shortcut that, use your keyboard arrow keys or a mouse cursor to select the item you want.
Beware Quotes In The Input / Option Values
One problem I’ve run into with this solution, is the accidental inclusion of a single or double quote in the value. This tends to create all kinds of havoc and chaos in the jQuery selector.
This is a screenshot from one of my actual projects – typing any special characters or quotes would cause this error to be triggered by the jQuery selector not being able to parse things correctly.
I haven’t found a solution for this, with the jQuery selector yet, but there’s probably something simple to prevent this from happening.
A Useful AutoComplete
I’ve generally found the DataList to be a useful component of HTML5, and have almost replaced all of my previous AJAX-style auto-complete situations.
There’s less code involved, since the list is stored in the HTML directly, but there are some limitations to this. You won’t want to store tens-of-thousands of options in your DataList as it would seriously bloat your page. For that, it may be better to continue using AJAX style auto-completes to filter the list down to something reasonable for selection.
Beyond that, and the use of data-id as shown above, the DataList has been a valuable addition to my tool belt. I suggest looking into it more for yourself, if you’re not already using it.