Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

229 lines (196 sloc) 10.448 kB

Create a Datalist Polyfill in Minutes

logo image

In this tutorial you will learn how to use HTML5 Datalists and also provide a fallback for older browsers. By the end of this article, you will create a highly customizable polyfill library to use for your projects.


Step 1: Developing with Native Support

Datalists introduced in HTML5, enhance your input tags, by providing suggestions for the most common/expected values. This makes your forms, faster to fill and with less errors (especially typos).

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Color Datalist</title>
</head>
<body>
    <input type="text" name="color" id="color" value="" list="colorlist" />
    <datalist id="colorlist">
        <option value="Black" />
        <option value="Blue" />
        <option value="Dark Green" />
        <option value="Grey" />
        <option value="Green" />
        <option value="Red" />
        <option value="White" />
        <option value="Yellow" />
    </datalist>
</body>
</html>

In an ideal world, where all your site visitors prefer modern browsers, some lines of HTML code are enough to produce a nice autocomplete feature. Current versions of Firefox, Chrome and Opera, support Datalists but their Implementations differs. Firefox shows suggestions that contain the input text, in contrast with Chrome and Opera showing only those that begin with it.

native support image


Step 2: Supporting older browsers

It's obvious that older browsers, without HTML5 support, will not give us the above result. You can easily check which browsers support Datalists at caniuse.com. To provide support for them, we will make a JavaScript polyfill library.

Some of them, including IE 9, will remove the option elements that we placed inside the datalist. So our first step is to preserve the option elements. This can easily be accomplished by surrounding them with a select element, as shown below.

...
<input type="text" name="color" id="color" value="" list="colorlist" />
<datalist id="colorlist">
    <select style="display: none;">
        <option value="Black" />
        <option value="Blue" />
        <option value="Dark Green" />
        <option value="Grey" />
        <option value="Green" />
        <option value="Red" />
        <option value="White" />
        <option value="Yellow" />
    </select>
</datalist>
...

IE9 before and after image

As you can see, we give the select element a "display: none;" style, so that the select input is not displayed. Note that, browsers supporting HTML5 datalist, will just skip the select element and continue to function as supposed.


Step 3: Include jQuery & jQuery UI

I'm going to use the jQuery UI autocomplete widget, which gives us an analogous behavior, is easy to use and yet very customizable. First of all we need to include jQuery and jQuery UI javascript libraries in our page. You can download local copies of the libraries from jquery.com and jqueryui.com or reference them directly from Google's CDN as I will demonstrate in my example code.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Color Datalist</title>
    <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" type="text/css" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
</head>
<body>
    <input type="text" name="color" id="color" value="" list="colorlist" />
    <datalist id="colorlist">
        <select style="display: none;">
            <option value="Black" />
            <option value="Blue" />
            <option value="Dark Green" />
            <option value="Grey" />
            <option value="Green" />
            <option value="Red" />
            <option value="White" />
            <option value="Yellow" />
        </select>
    </datalist>
    <script>
    $(document).ready(function () {
        var availableTags = $('#colorlist').find('option').map(function () {
            return this.value;
        }).get();
        $('#color').autocomplete({ source: availableTags });
    });
    </script>
</body>
</html>

As you can see, the code needed to initialize the autocomplete widget is quite compact. We first create the list of suggestions by reading the datalist's options elements, and then call the autocomplete function on the desired input element including a the list as a parameter.


Step 4: Turning it into a polyfill

The main idea of a polyfill is that it runs only when a feature is missing from a browser, letting modern browsers use their (faster) native implementation. The first line of the code below determines if there is support for HTML5 datalist. After that and only if support is absent, we initialize the autocomplete widget, for each input element of the page with the appropriate datalist.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Color Datalist</title>
    <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css" type="text/css" />
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js"></script>
</head>
<body>
    <input type="text" name="color" id="color" value="" list="colorlist" />
    <datalist id="colorlist">
        <select style="display: none;">
            <option value="Black" />
            <option value="Blue" />
            <option value="Dark Green" />
            <option value="Grey" />
            <option value="Green" />
            <option value="Red" />
            <option value="White" />
            <option value="Yellow" />
        </select>
    </datalist>
    <script>
    $(document).ready(function () {
        var nativedatalist = !!('list' in document.createElement('input')) && 
            !!(document.createElement('datalist') && window.HTMLDataListElement);

        if (!nativedatalist) {
            $('input[list]').each(function () {
                var availableTags = $('#' + $(this).attr("list")).find('option').map(function () {
                    return this.value;
                }).get();
                $(this).autocomplete({ source: availableTags });
            });
        }
    });
    </script>
</body>
</html>

The result, as you can see, is quite close to browsers with native support. Moreover you are free to style the appearance of the list with css or jQuery ThemeRoller. Also note that depending on the size of your project, you might better be using libraries like Modernizr to detect the supporting features of a browser.

IE and safari

The above javascript code could also (or better should) be placed in a separate .js file. That way, just by including that file in any page (below its dependencies), you provide support to all datalists used in the page, no matter what browser the user is using.


Step 5: Conditional async loading with Modernizr (optional)

In this step I'm going to use Modernizr for feature detection and conditional async loading of the polyfill. So first of all download a custom build of Modernizr with (at least) "Input Attributes", "Input Types" and "Modernizr.load" checked, as shown below. I also like to include html5shiv so that HTML5 elements (like section, nav, header, footer and article) work on old browsers. Click Generate and Download the custom build, which will get a name like modernizr.custom.xxxxx.js (where xxxxx will be five random numbers).

Modernizr build

Using Modernizr makes feature detection much simpler and accurate. It also has built in yepnope.js, to provide an easy conditional asynchronous loading of resources. To take advantage of the conditional loading, we first separate the polyfill's logic from our html code, in a separate DatalistPolyfill.js file. Your DatalistPolyfill.js file should look like this.

$(document).ready(function () {
    $('input[list]').each(function () {
        var availableTags = $('#' + $(this).attr("list")).find('option').map(function () {
            return this.value;
        }).get();
        $(this).autocomplete({ source: availableTags });
    });
});

Next, place DatalistPolyfill.js and modernizr.custom.xxxxx.js in a folder named 'js', next to your html file. Remove all the resource references (js & css) from your html and add just a script tag for modernizer and one more to initialize it.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Color Datalist</title>
    <script src="js/modernizr.custom.95515.js"></script>
    <script>
        Modernizr.load([
            'https://ajax.googleapis.com/ajax/libs/jquery/1.7/jquery.min.js',
            {
                test : Modernizr.input.list,
                nope : ['https://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css',
                        'http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/jquery-ui.min.js',
                        'js/DatalistPolyfill.js']
            }
        ]);
    </script>
</head>
<body>
    <input type="text" name="color" id="color" value="" list="colorlist" />
    <datalist id="colorlist">
        <select style="display: none;">
            <option value="Black" />
            <option value="Blue" />
            <option value="Dark Green" />
            <option value="Grey" />
            <option value="Green" />
            <option value="Red" />
            <option value="White" />
            <option value="Yellow" />
        </select>
    </datalist>
</body>
</html>

Finally note that:

  • The above code will always load jQuery.
  • Only when we need to polyfill datalist, it will also load jquery-ui.css, jquery-ui.min.js, and the polyfill's initialization script.
  • Placing those scripts in the head element will benefit your performance, since the async loading will start as soon as possible.
  • Since all the yepnope functionality can be accessed through Modernizr.load function, you might be interested reading this tutorial of yepnope.
Jump to Line
Something went wrong with that request. Please try again.