Tags: datalist

2

sparkline

Generating placeholders from datalists

Here’s a cute little markup pattern for ya.

Suppose you’ve got an input element that has—by means of a list attribute—an associated datalist. Here’s the example I used in HTML5 For Web Designers:

<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>

That results in a combo-box control in supporting browsers: as you type in the text field, you are presented with a subset of the options in the datalist that match what you are typing. It’s more powerful than a regular select, because you aren’t limited by the list of options: you’re free to type something that isn’t in the list (like, say, “Pluto”).

I’ve already written about the design of datalist and how you can use a combination of select and input using the same markup to be backward-compatible. I like datalist.

I also like the placeholder attribute. Another recent addition to HTML, this allows you to show an example of the kind of content you’d like the user to enter (note: this is not the same as a label).

It struck me recently that all the options in a datalist are perfectly good candidates for placeholder text. In the example above, I could update the input element to include:

<input type="text" name="homeworld" id="homeworld" list="planets" placeholder="Mars">

or:

<input type="text" name="homeworld" id="homeworld" list="planets" placeholder="Saturn">

I wrote a little piece of JavaScript to do this:

  1. Loop through all the input elements that have a list attribute.
  2. Find the corresponding datalist element (its ID will match the list attribute).
  3. Pick a random option element from that datalist.
  4. Set the placeholder value of the input to that option value.

Put that JavaScript at the end of your document (or link to it from the end of your document) and you’re all set. You might want to tweak it a little: I find it helps to preface placeholder values with “e.g.” to make it clear that this is an example value. You can do that by changing the last line of the script:

input.setAttribute('placeholder','e.g. '+value);

You also might want to show more than one possible value. You might want the placeholder value to read “e.g. Mercury, Venus, Earth, etc.” …I’ll leave that as an exercise for the reader.

The design of datalist

One of the many form enhancements provided by HTML5 is the datalist element. It allows you to turn a regular input field into a .

Using the list attribute on an input, you can connect it to a datalist with the corresponding ID. The datalist itself contains a series of option elements.

<input list="suggestions">
<datalist id="suggestions">
    <option value="foo"></option>
    <option value="bar"></option>
    <option value="baz"></option>
</datalist>

I can imagine a number of use cases for this:

  • “Share this” forms, like the one on Last.fm, that allow you to either select from your contacts on the site, or enter email addresses, separated by commas. Using input type="email" with a multiple attribute, in combination with a datalist would work nicely.
  • Entering the details for an event, where you can either select from a list of venues or, if the venue is not listed, create a new one.
  • Just about any form that has a selection of choices, of which the last choice is “other”, followed by “If other, please specify…”

You can take something like this:

<label for="source">How did you hear about us?</label>
<select name="source">
    <option>please choose...</option>
    <option value="television">Television</option>
    <option value="radio">Radio</option>
    <option value="newspaper">Newspaper</option>
    <option>Other</option>
</select>
If other, please specify:
<input id="source" name="source">

And replace it with this:

<label for="source">How did you hear about us?</label>
<datalist id="sources">
    <option value="television"></option>
    <option value="radio"></option>
    <option value="newspaper"></option>
</datalist>
<input id="source" name="source" list="sources">

The datalist element has been designed according to one of the design principles driving HTML5—Degrade Gracefully:

On the World Wide Web, authors are often reluctant to use new language features that cause problems in older user agents, or that do not provide some sort of graceful fallback. HTML 5 document conformance requirements should be designed so that Web content can degrade gracefully in older or less capable user agents, even when making use of new elements, attributes, APIs and content models.

Because the datalist element contains a series of option elements with value attributes, it is effectively invisible to user-agents that don’t support the datalist element. That means you can use the datalist element without worrying it “breaking” in older browsers.

If you wanted, you could include a message for non-supporting browsers:

<datalist id="sources">
    Your browser doesn't support datalist!
    <option value="television"></option>
    <option value="radio"></option>
    <option value="newspaper"></option>
</datalist>

That message—“Your browser doesn’t support datalist!”—will be visible in older browsers, but browsers that support datalist know not to show anything that’s not an option. But displaying a message like this for older browsers is fairly pointless; I certainly wouldn’t consider it graceful degradation.

In my opinion, one of the best aspects of the design of the datalist element is that you can continue to do things the old-fashioned way—using a select and an input—and at the same time start using datalist. There’s no violation of either; you can use the same option elements for the select and the datalist:

<label for="source">How did you hear about us?</label>
<datalist id="sources">
    <select name="source">
        <option>please choose...</option>
        <option value="television">Television</option>
        <option value="radio">Radio</option>
        <option value="newspaper">Newspaper</option>
        <option>Other</option>
    </select>
    If other, please specify:
</datalist>
<input id="source" name="source" list="sources">

Browsers that support datalist will display the label “How did you hear about us?” followed by a combo-box text field that allows the user to select an option, or enter some free text.

Browsers that don’t support datalist will display the the label “How did you hear about us?” followed by a drop-down list of of options (the last of which is “other”), followed by the text “If other, please specifiy”, followed by a text field.

Take a look at this example in Opera to see datalist in operation. Take a look at it in any other browser to see the fallback. The source is on Github if you’d like to play around with it.

WebKit’s mistake

If you try that example in any browser, you’ll get something that works; either through datalist support or through the select fallback …unless the browser is using WebKit.

It took me a while to figure out why this would be the case. I didn’t think that Safari or Chrome supported datalist and a little digging around with object detection in JavaScript confirmed this. So why don’t those browsers follow the standard behaviour and simply ignore the element they don’t understand and render what’s inside it instead.

Here’s the problem: line 539 of WebKit’s default CSS:

datalist {
    display: none;
}

This is pretty much the worst possible behaviour for a browser to implement. An element should either be recognised—like p, h1 or img—and parsed accordingly, or an element is unrecognised—like foo or bar—and ignored. WebKit does not support the datalist element (even in the current nightly build), so the element should be ignored.

Fortunately the problem is easily rectified by adding something like this to your own stylesheet:

datalist {
    display: inline-block;
}

I’ve submitted a bug report on the WebKit Bugzilla.

Update: That Webkit bug has now been fixed so the extra CSS is no longer necessary.