JavaScript
Article

Building a JavaScript Autocomplete Widget with Awesomplete

By Narayan Prusty

Autocomplete is a feature in web applications which predicts the rest of a word or sentence while the user is still typing. Users usually press the tab key to accept a suggestion, or the down arrow key to accept one of several.

In this tutorial we will look at how to use the Awesomplete JavaScript library to create an autocomplete widget in our websites. Awesomplete is created by Lea Verou, a well-known speaker, author and invited expert in the W3C CSS working group.

Why Not Use the HTML5 datalist Element?

The HTML5 datalist element is possibly the simplest way to implement an autocomplete feature in a website. Unfortunately, browser support for this element is limited and it’s implementation is inconsistent (e.g. Chrome matches only from the start, Firefox anywhere). It’s also not possible to style it according to your website’s design and, although promising, it’s probably not the right choice yet.

Awesomplete, on the other hand, is an ultra lightweight, customizable autocomplete widget, which you can drop into your pages. It has zero dependencies (no jQuery), works across all modern browsers and can be styled according to your website’s theme.

So what are we waiting for? Let’s dive in!

Including Awesomplete in Your Web Page

To use the Awesomplete library we need two files: awesomplete.css and awesomplete.js.

You can get these using bower:

bower install https://github.com/LeaVerou/awesomplete.git#gh-pages

By downloading them from the Awesomplete website directly.

Or, by including them via the RawGit CDN (which serves raw files directly from GitHub with proper Content-Type headers). This is demonstrated below.

To instantiate the basic widget, you need an input element with a class of awesomplete, followed by the assosciated options in a datalist element. The list attribute of the input element must match the id of the datalist element. This is a sensible default configuration, as it offers a fallback to any users with JavaScript disabled.

<!doctype html>
<html>
  <head>
    <link rel="stylesheet" href="https://cdn.rawgit.com/LeaVerou/awesomplete/gh-pages/awesomplete.css">
  </head>
  <body>
    <input class="awesomplete" list="mylist" />
    <datalist id="mylist">
      <option>One</option>
      <option>Two</option>
      <option>Three</option>
    </datalist>

    <script src="https://cdn.rawgit.com/LeaVerou/awesomplete/gh-pages/awesomplete.min.js"></script>
  </body>
</html>

Basic Functionality

There are many ways to use this versatile library. Let’s start with a basic use case.

Using the data-list Attribute

It is possible to move the options from the aforementioned datalist element into a data-list attribute on the input element itself.

<input class="awesomplete"
       data-minchars="1"
       data-maxitems="5"
       data-list="China, India, Japan, Russia, UK, USA" />

See the Pen Awesomplete (1) by SitePoint (@SitePoint) on CodePen.

Using JavaScript

The above solutions are useful if your autocomplete options are static. However. to create the list dynamically and further customize the behavior of the autocomplete widget, we need a JavaScript method.

<input id="countries" />

var input = document.getElementById("countries");
var awesomplete = new Awesomplete(input, {
  minChars: 1,
  maxItems: 5,
  autoFirst: true
});
awesomplete.list = ["China", "India", "Japan", "Russia", "UK", "USA"];

Here we are creating a Awesomplete object, passing it two parameters: a reference to our input element, and an object literal containing the configuration options.

We then assign the list property of our Awesomplete object to an array holding the list of the autocomplete options. In the demo below, I have expanded the array of country names, using this handy snippet.

Also, note that the minChars, maxItems and autoFirst properties are same as the data-minchars, data-maxitems and data-autofirst attributes in the previous demo.

See the Pen Awesomplete (2) by SitePoint (@SitePoint) on CodePen.

When using JavaScript to instantiate an autocomplete widget we have access to many other properties, APIs and events. Let see what are they are how to use them?

Extended JavaScript Properties

There are four other properties supported by the Awesomplete object. They are: filter, sort, item and replace. These four properties have functions assigned to them.

The filter property controls how entries get matched. Its callback function takes two parameters: the current suggestion text (so in our example each of the values “China”, “India”, “Japan”, “Russia”, “UK”, “USA” in turn) and a string containing the user’s input. By default, the input can match anywhere within the string and it’s a case insensitive match.

The following example demonstrates how you would make Awesomplete perform a case-sensitive match:

function myFilterFunc(text, input) {
  return text.indexOf(input) > -1;
}

var input = document.getElementById("countries");
var awesomplete = new Awesomplete(input, {
  filter: myFilterFunc
});
awesomplete.list = ["China", "India", "Japan", "Russia", "UK", "USA"];

The sort property controls how list items are ordered. Its callback has the same prototype as the Array.prototype.sort() function.

Here’s how you would use it to sort matches in reverse alphabetical order:

function mySortFunc(text, input) {
  return text < input;
}

var input = document.getElementById("countries");
var awesomplete = new Awesomplete(input, {
  sort: mySortFunc
});
awesomplete.list = ['Albania', 'Brazil', 'Chile', 'Denmark', 'Egypt'];

The item property controls how list items are generated. This callback also has two arguments: the current suggestion text and user’s input. It should return a list item. Here’s how you would use the item property to highlight user input within a suggestion text:

function myItemFunc(text, input){
  return Awesomplete.$.create("li", {
    innerHTML: text.replace(RegExp(input.trim(), "gi"), "<mark>$&amp;</mark>"),
    "aria-selected": "false"
  });
}

var input = document.getElementById("countries");
var awesomplete = new Awesomplete(input, {
  item: myItemFunc
});
awesomplete.list = ["China", "India", "Japan", "Russia", "UK", "USA"];

The fourth and final property is the replace property. The replace property controls how the user’s selection replaces the user’s input. In contrast to the previous three functions, this callback takes one parameter: the text of the selected option. It is fired when the user selects one of the suggested options (e.g. by clicking on it).

Here’s how you would use it to transform a user’s selection to upper case:

function myReplaceFunc(text) {
  input.value = text.toUpperCase();
}

var input = document.getElementById("countries");
var awesomplete = new Awesomplete(input, {
  replace: myReplaceFunc
});
awesomplete.list = ['Albania', 'Brazil', 'Chile', 'Denmark', 'Egypt'];

Tying It All Together

Here is an demo showing how to combine the filter and item functions to only make a suggestion once a user has entered a specified character(s) (in this case a comma followed by a space):

See the Pen Awesomplete (3) by SitePoint (@SitePoint) on CodePen.

Digging Deeper — Events, APIs and Ajax

There are five custom events that are fired by this library. These are: awesomplete-select, awesomplete-selectcomplete, awesomplete-open, awesomplete-close and awesomplete-highlight.

This is how you would hook into each of these events:

window.addEventListener("awesomplete-select", function(e){
  // User made a selection from dropdown. 
  // This is fired before the selection is applied
}, false);

window.addEventListener("awesomplete-selectcomplete", function(e){
  // User made a selection from dropdown. 
  // This is fired after the selection is applied
}, false);

window.addEventListener("awesomplete-open", function(e){
  // The popup just appeared.
}, false);

window.addEventListener("awesomplete-close", function(e){
  // The popup just closed.
}, false);

window.addEventListener("awesomplete-highlight", function(e){
  // The highlighted item just changed 
  // (in response to pressing an arrow key or via an API call).
}, false);

Awesomplete provides various methods on the Awesomplete object that allow you to customize its behavior:

  1. open(): Used to open the popup.
  2. close(): Used to close the popup.
  3. next(): Used to highlight the next item in the popup.
  4. previous(): Used to highlight the previous item in the popup.
  5. goto(i): Used to highlight the item with index i in the popup (-1 to deselect all).
  6. select(): Used to select the currently highlighted item, replace the text field’s value with it and close the popup.
  7. evaluate(): Used to evaluate the current state of the widget and regenerate the list of suggestions. Closes the popup if none are available. This method is particularly useful if you dynamically set the list property while the popup is open.

Note: open() will not currently open the list before an input event has fired, but there is a pull request on the project’s home page which should fix this.

The Finale

By way of a final example, this is how you might use Awesomplete in conjunction with data fetched from a remote API via Ajax. I will be using the REST Countries API, which provides users with a whole host of country data.

The first thing to do is to initialize the widget without setting its list property (I’m using jQuery here for the sake of brevity):

var input = $("#countries");
var awesomplete = new Awesomplete(input, {
  minChars: 1,
  autoFirst: true
});

Then, attach a keyup event listener:

$(input).on("keyup", function(){ ... }

When the user has pressed a key, we need to grab the value of the input element and make our request:

$.ajax({
  url: 'https://restcountries.eu/rest/v1/name/' + this.value,
  type: 'GET',
  dataType: 'json'
})

Within the success callback, we can iterate over the JSON response, grab the names of the respective cities and set the list property of the Awesomplete object on the fly:

.success(function(data) {
  var list = [];
  $.each(data, function(key, value) {
    list.push(value.name);
  });
  awesomplete.list = list;
});

And that’s it!

See the Pen Awesomplete (4) by SitePoint (@SitePoint) on CodePen.

Conclusion

In this tutorial, we have seen how easily we can implement an autocomplete widget in our projects using the lightweight and customizable Awesomplete library. The project is still being actively maintained and I encourage you to check it out.

  • Roman

    Does this work well on smartphones?

    • http://qnimate.com/ Narayan Prusty

      Yes it does.

      • Roman

        The virtual keyboard doesn’t cover the drop-down list with suggestions?

        • http://qnimate.com/ Narayan Prusty

          Its adjusted automatically. Try it once.

  • Iswan Jumat

    Grate but for me typeaheadJs is best https://twitter.github.io/typeahead.js/

    • Gayan Charith Witharana

      yeah, that’s great ! I am also using this library.

  • http://www.mithunjj.com/ Mithun John Jacob
  • Baccouch Taieb

    Thank you for this great tutorial.

  • http://kenne.be Ken Verhaegen

    “Users usually press the tab key to accept a suggestion, or the down arrow key to accept one of several.” None of these demo’s allow the tab. Yes, the arrow down works… but in the last one it keeps going to the first element (refreshing?). Ridiculous xD.

  • shiru

    how to use autocomplete by fetching database value

  • Dhiraj Mohapatra

    Great Tutorial But Narayan I have some problem in this like “,” or “Comma”. I have a list of data and many list have “comma” in the list. But awesomplete separate that list into two which is incorrect as per my requirement. Please suggest me to get out of this stuff.

  • Imamadmad

    This sounds great… but it doesn’t work. Even your own examples do nothing. So, while I would love to use your widget, I will have to pass it by for something that works. I’m using IE11 if that’s any help.

  • RhettKoe

    2KB autocomplete with zero dependencies… pretty much says it all.. great plugin, super easy to use.

  • Dominik

    Please note that the JS snippet in “The Finale” is slightly different, in the text you mention “$(“#countries”)”, but in the code you actually use “document.getElementById(“countries”)”, took me a while to find that one :(

  • RhettKoe

    Awesome.. .thank you!

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in JavaScript, once a week, for free.