Dynamic Datalist: Autocomplete from an API :: Aaron Gustafson
Great minds think alike! I have a very similar HTML web component on the front page of The Session called input-autosuggest.
Great minds think alike! I have a very similar HTML web component on the front page of The Session called input-autosuggest.
A good overview of how large language models work:
The words flow together because they’ve been seen together many times. But that doesn’t mean they’re right. It just means they’re coherent.
LLMs are good at transforming text into less text
Laurie is really onto something with this:
This is the biggest and most fundamental thing about LLMs, and a great rule of thumb for what’s going to be an effective LLM application. Is what you’re doing taking a large amount of text and asking the LLM to convert it into a smaller amount of text? Then it’s probably going to be great at it. If you’re asking it to convert into a roughly equal amount of text it will be so-so. If you’re asking it to create more text than you gave it, forget about it.
Depending how much of the hype around AI you’ve taken on board, the idea that they “take text and turn it into less text” might seem gigantic back-pedal away from previous claims of what AI can do. But taking text and turning it into less text is still an enormous field of endeavour, and a huge market. It’s still very exciting, all the more exciting because it’s got clear boundaries and isn’t hype-driven over-reaching, or dependent on LLMs overnight becoming way better than they currently are.
The datalist element is good. It was a bit bumpy there for a while, but browser implementations have improved over time. Now it’s by far the simplest and most robust way to create an autocompleting combobox widget.
Hook up an input element with a datalist element using the list and id attributes and you’re done. You can even use a bit of Ajax to dynamically update the option elements inside the datalist in response to the user’s input. The browser takes care of all the interaction. If you try to roll your own combobox implementation, it’s almost certainly going to involve a lot of JavaScript and still probably won’t account for all use cases.
Safari on iOS—and therefore all browsers on iOS—didn’t support datalist for quite a while. But once it finally shipped, it worked really nicely. The options showed up just like automplete suggestions above the keyboard.
But that broke a while back.
The suggestions still appeared, but if you tapped on one of them, nothing happened. The input element didn’t get updated. You had to tap on a little downward arrow inside the input in order to see the list of options.
That was really frustrating for anybody on iOS using The Session. By far the most common task on the site is searching for a tune, something that’s greatly (progressively) enhanced with a dynamically-updating datalist.
I just updated to iOS 18 specifically to see if this bug has been fixed, and it has:
Fixed updating the input value when selecting an
optionfrom adatalistelement.
Hallelujah!
But now there’s some additional behaviour that’s a little weird.
As well as showing the options in the autocomplete list above the keyboard, Safari on iOS—and therefore all browsers on iOS—also pops up the options as a list (as if you had tapped on that downward arrow). If the list is more than a few options long, it completely obscures the input element you’re typing into!
I’m not sure if this is a bug or if it’s the intended behaviour. It feels like a bug, but I don’t know if I should file something.
For now, I’ve updated the datalist elements on The Session to only ever hold three option elements in order to minimise the problem. Seeing as the autosuggest list above the keyboard only ever shows a maximum of three suggestions anyway, this feels like a reasonable compromise.
Garrett talks through some handy HTML attributes: spellcheck, autofocus, autocapitalize, autocomplete, and autocorrect:
While they feel like small details, when we set these attributes on inputs, we streamline things for visitors while also guiding the browser on when it should just get out of the way.
Forget every article you’ve read that tries to explain large language models. Just read this post by Peter and feel it.
Forms on the web are an opportunity to make big improvements to the user experience with very little effort. The effort can be as little as sprinkling in a smattering of humble HTML attributes. But the result can be a turbo-charged experience for the user, allowing them to sail through their task.
This is particularly true on mobile devices where people have to fill in forms using a virtual keyboard. Any improvement you can make to their flow is worth investigating. But don’t worry: you don’t need to add a complex JavaScript library or write convoluted code. Well-written HTML will get you very far.
If you’re using the right input type value, you’re most of the way there. Browsers on mobile devices can use this value to infer which version of the virtual keyboard is best. So think beyond the plain text value, and use search, email, url, tel, or number when they’re appropriate.
But you can offer more hints to those browsers. Here are three attributes you can add to input elements. All three are enumerated values, which means they have a constrained vocabulary. You don’t need to have these vocabularies memorised. You can look them when you need to.
inputmodeThe inputmode attribute is the most direct hint you can give about the virtual keyboard you want. Some of the values are redundant if you’re already using an input type of search, email, tel, or url.
But there might be occasions where you want a keyboard optimised for numbers but the input should also accept other characters. In that case you can use an input type of text with an inputmode value of numeric. This also means you don’t get the spinner controls on desktop browsers that you’d normally get with an input type of number. It can be quite useful to supress the spinner controls for numbers that aren’t meant to be incremented.
If you combine inputmode="numeric" with pattern="[0-9]", you’ll get a numeric keypad with no other characters.
The list of possible values for inputmode is text, numeric, decimal, search, email, tel, and url.
enterkeyhintWhereas the inputmode attribute provides a hint about which virtual keyboard to show, the enterkeyhint attribute provides an additional hint about one specific key on that virtual keyboard: the enter key.
For search forms, you’ve got an enterkeyhint option of search, and for contact forms, you’ve got send.
The enterkeyhint only changes the labelling of the enter key. On some browsers that label is text. On others it’s an icon. But the attribute by itself doesn’t change the functionality. Even though there are enterkeyhint values of previous and next, by default the enter key will still submit the form. So those two values are less useful on long forms where the user is going from field to field, and more suitable for a series of short forms.
The list of possible values is enter, done, next, previous, go, search, and send.
autocompleteThe autocomplete attribute doesn’t have anything to do with the virtual keyboard. Instead it provides a hint to the browser about values that could pre-filled from the user’s browser profile.
Most browsers try to guess when they can they do this, but they don’t always get it right, which can be annoying. If you explicitly provide an autocomplete hint, browsers can confidently prefill the appropriate value.
Just think about how much time this can save your users!
There’s a name value you can use to get full names pre-filled. But if you have form fields for different parts of names—which I wouldn’t recommend—you’ve also got:
given-name,additional-name,family-name,nickname,honorific-prefix, andhonorific-suffix.You might be tempted to use the nickname field for usernames, but no need; there’s a separate username value.
As with names, there’s a single tel value for telephone numbers, but also an array of sub-values if you’ve split telephone numbers up into separate fields:
tel-country-code,tel-national,tel-area-code,tel-local, andtel-extension.There’s a whole host of address-related values too:
street-address,address-line1,address-line2, andaddress-line3, but alsoaddress-level1,address-level2,address-level3, andaddress-level4.If you have an international audience, addresses can get very messy if you’re trying to split them into separate parts like this.
There’s also postal-code (that’s a ZIP code for Americans), but again, if you have an international audience, please don’t make this a required field. Not every country has postal codes.
Speaking of countries, you’ve got a country-name value, but also a country value for the country’s ISO code.
Remember, the autocomplete value is specifically for the details of the current user. If someone is filling in their own address, use autocomplete. But if someone has specified that, say, a billing address and a shipping address are different, that shipping address might not be the address associated with that person.
On the subject of billing, if your form accepts credit card details, definitely use autocomplete. The values you’ll probably need are:
cc-name for the cardholder,cc-number for the credit card number itself,cc-exp for the expiry date, andcc-csc for the security again.Again, some of these values can be broken down further if you need them: cc-exp-month and cc-exp-year for the month and year of the expiry date, for example.
The autocomplete attribute is really handy for log-in forms. Definitely use the values of email or username as appropriate.
If you’re using two-factor authentication, be sure to add an autocomplete value of one-time-code to your form field. That way, the browser can offer to prefill a value from a text message. That saves the user a lot of fiddly copying and pasting. Phil Nash has more details on the Twilio blog.
Not every mobile browser offers this functionality, but that’s okay. This is classic progressive enhancement. Adding an autocomplete value won’t do any harm to a browser that doesn’t yet understand the value.
Use an autocomplete value of current-password for password fields in log-in forms. This is especially useful for password managers.
But if a user has logged in and is editing their profile to change their password, use a value of new-password. This will prevent the browser from pre-filling that field with the existing password.
That goes for sign-up forms too: use new-password. With this hint, password managers can offer to automatically generate a secure password.
There you have it. Three little HTML attributes that can help users interact with your forms. All you have to do was type a few more characters in your input elements, and users automatically get a better experience.
This is a classic example of letting the browser do the hard work for you. As Andy puts it, be the browser’s mentor, not its micromanager:
Give the browser some solid rules and hints, then let it make the right decisions for the people that visit it, based on their device, connection quality and capabilities.
This post has also been translated into French.
I completely agree with Dan that when it comes to design systems, completeness is an over-rated—and even counter-productive—goal:
Some organizations seem to hold up the ideal that, once a design system exists, everything in an interface can and should be built with it. Not only is that an unrealistic goal for most enterprises, but it can often be a toxic mindset that anything less than 100% coverage is misuse of a design system at best or utter failure at worst.
I remember Lara telling me a great quote from the Clarity conference a few years back: “A design system needs to be correct before it’s complete.” In other words, it’s better to have one realistic component that’s actually in production than to have a pattern library full of beautiful but unimplemented components. I feel like Robin is getting at much the same point here, but he frames it in terms of correctness and usefulness:
If we want to be correct, okay, let’s have components of everything and an enormous Figma library of stuff we need to maintain. But if we want to be useful to designers who want to get an understanding of the system, let’s be brief.
This is such a handy tool for building forms! Choose different combinations of type, inputmode, and autocomplete attributes on input elements and see how that will be conveyed to users on iOS and Android devices.
I’m a big fan of HTML5’s datalist element and its elegant design. It’s a way to progressively enhance any input element into a combobox.
You use the list attribute on the input element to point to the ID of the associated datalist element.
<label for="homeworld">Your home planet</label>
<input type="text" name="homeworld" id="homeworld" list="planets">
<datalist id="planets">
<option value="Mercury">
<option value="Venus">
<option value="Earth">
<option value="Mars">
<option value="Jupiter">
<option value="Saturn">
<option value="Uranus">
<option value="Neptune">
</datalist>
It even works on input type="color", which is pretty cool!
The most common use case is as an autocomplete widget. That’s how I’m using it over on The Session, where the datalist is updated via Ajax every time the input is updated.
But let’s stick with a simple example, like the list of planets above. Suppose the user types “jup” …the datalist will show “Jupiter” as an option. The user can click on that option to automatically complete their input.
It would be handy if you could automatically submit the form when the user chooses a datalist option like this.
Well, tough luck.
The datalist element emits no events. There’s no way of telling if it has been clicked. This is something I’ve been trying to find a workaround for.
I got my hopes up when I read Amber’s excellent article about document.activeElement. But no, the focus stays on the input when the user clicks on an option in a datalist.
So if I can’t detect whether a datalist has been used, this best I can do is try to infer it. I know it’s not exactly the same thing, and it won’t be as reliable as true detection, but here’s my logic:
Here’s how that translates into DOM scripting code:
document.querySelectorAll('input[list]').forEach( function (formfield) {
var datalist = document.getElementById(formfield.getAttribute('list'));
var lastlength = formfield.value.length;
var checkInputValue = function (inputValue) {
if (inputValue.length - lastlength > 1) {
datalist.querySelectorAll('option').forEach( function (item) {
if (item.value === inputValue) {
formfield.form.submit();
}
});
}
lastlength = inputValue.length;
};
formfield.addEventListener('input', function () {
checkInputValue(this.value);
}, false);
});
I’ve made a gist with some added feature detection and mustard-cutting at the start. You should be able to drop it into just about any page that’s using datalist. It works even if the options in the datalist are dynamically updated, like the example on The Session.
It’s not foolproof. The inference relies on the difference between what was previously typed and what’s autocompleted to be more than one character. So in the planets example, if someone has type “Jupite” and then they choose “Jupiter” from the datalist, the form won’t automatically submit.
But still, I reckon it covers most common use cases. And like the datalist element itself, you can consider this functionality a progressive enhancement.
Here’s one simple, practical way to make apps perform better on mobile devices: always configure HTML input fields with the correct
type,inputmode, andautocompleteattributes. While these three attributes are often discussed in isolation, they make the most sense in the context of mobile user experience when you think of them as a team.
This is an excellent deep dive with great advice:
You may think that you are familiar with the basic
autocompleteoptions, such as those that help the user fill in credit card numbers or address form fields, but I’d urge you to review them to make sure that you are aware of all of the options. The spec lists over 50 values!
The many ways of improving a single form field in HTML.
I love these kinds of deep dives into markup!
When I liveblogged Jason’s talk at An Event Apart in Chicago, I included this bit of reporting:
Jason proceeds to relate a long and involved story about buying burritos online from Chipotle.
Well, here is that story. It’s a good one, with some practical takeaways (if you’ll pardon the pun):
- Use HTML5 input features
- Support autofill
- Make autofill part of your test plans
This starts as a good bit of computer science nerdery, that kind of answers the question in the title:
Alone, CSS is not Turing complete. CSS plus HTML plus user input is Turing complete!
And so the takeaway here is bigger than just speculation about Turing completeness:
Given that CSS is a domain-specific language for styling user interface, this makes a lot of sense! CSS + HTML + Human = Turing complete.
At the end of that day, as CSS developers that is the language we really write. CSS is incomplete without HTML, and a styled interface is incomplete without a human to use it.
Testing the theory that putting the word “total”, “complete”, or “absolute” in front of any noun automatically makes for an excellent insult.
If you’re looking for an accessible standalone autocomplete script, this one from GDS looks very good (similar to Lea’s awesomplete).
This is a really good use-case for cancelling fetch requests: making API calls while autocompleting in search.
A few straightforward steps for improving the usability of credit card forms. The later steps involve JavaScript but the first step uses nothing more than straight-up HTML.
Equal parts clever and scary. By using autocomplete in HTML and some offscreen positioning in CSS, it’s possible to extract some unexpected personal information.
I expect browsers will be closing these holes pretty quickly.