Click here to monitor SSC
  • Av rating:
  • Total votes: 13
  • Total comments: 0
Dino Esposito

Auto-completion in HTML-Based Input Forms

13 June 2013

Are there ever times when the best practice for a GUI is to let the user type-in information using the keyboard? Of course there are, but then the users nowadays expect, when it is appropriate, to have  auto-completion  and suggestions that come with the search engines such as Google, and from mobile computers. The GUI must never get in the way. Dino shows how, as usual.

In a comment to a recent article of mine titled “Modal Input Forms in ASP.NET MVC”, a reader wrote “What most people don't realize is that a good interface is invisible to the user. Getting the work done is what is most important.” I’d add that the role of the UI is just to streamline the tasks without itself becoming a bottleneck, and without affecting the way in which users usually do things.

As emphatic as it may sound, the Internet and search engines are slowly but steadily changing the way that we study and learn things. We can now avoid the trouble of finding a topic in a table of contents by typing whatever might possibly match our target argument and then letting Google do the job.

This pattern is taking hold, and users now expect a far easier route to information. What does it mean to you when you’re creating an application? For instance, it means that you should, ideally, pay more attention to input fields and auto-completion.

Typing is Boring but Usually Faster

The mouse was a great invention, but it didn’t, and will not, kill the keyboard. Some of the actions that the user performs in an input form are ideally accomplished with the mouse; and there are actions that couldn’t be performed faster than with a keyboard. About a year ago, in the context of a mobile site project, we interviewed a group of users about their fastest way to enter a date over a smartphone. Quite surprisingly, about half the people said they would have liked to just type it as ddmmyy in a plain text box and have the software split that text into a valid date. If you find it hard to believe, then let me add that the use-case was for the entry of just about any date and not necessarily dates close to the current day. Imagine what it takes to repeatedly scroll with the finger (or the mouse) to find a birth date of some 40 years ago. In my opinion, this is one of those little things that make the UI “invisible” to the user. Date pickers are a great thing, and I blame Internet Explorer 10 for not having them implemented natively, but it’s up to the good UX designer to understand when a plain text box is actually a better choice.

A Better Way to Pick a Country

How many times did you write a form to select a country name? And how many times did you end up with an apparently endless drop-down list? A million web sites out there do just that. But is it really the most usable way of doing that? Look carefully at what users (including yourself) really do in order to select a country from a long list: they move the focus to the drop-down and then quickly type the first letters of the country name. If the name you want isn't already selected in the drop-down list, then it’s only one or two keyboard hits away.

Typing is boring, but once you decide to put your hands on the keyboard it may be faster than ever for certain tasks.

Why don’t we use a plain text box for entering a country name? The primary reason is that we need no ambiguity in country names: for humans, USA is the same as United States and sometimes United Kingdom may be as acceptable as Great Britain. The language is also an issue: Italia is not the same for machines as Italy for example. Finally, users can mistype names in a free text box whereas, with a drop-down list, the page provides a fixed list of names and, more importantly, each entry is bound to a unique ID that unambiguously identifies the selected country.

What would be a better way to pick up the name of a country? Something like an auto-completion textbox, perhaps.

Extensions for auto-completion

Browsers offer a default form of auto-completion that you can disable by placing the autocomplete attribute to off.

<input type="text" autocomplete="off" />

When enabled, though, the default auto-completion only retrieves data from an internal cache of strings that the user typed in the same field in the same page. As you can see, there’s no opportunity for control by the developer over the suggestions provided.

The HTML5 standard includes a new element—the DATALIST element—that serves the purpose of auto-completion to some extent. The DATALIST element is a specialized version of the popular SELECT element. It provides the same behavior except that the drop-down list applies to a text input field. Here’s an example:

<input type="text" list="countries" />
<datalist id="countries"> 
   <option value="Italy"> 
   <option value="Sweden"> 
   <option value="Denmark">
</datalist>

You define a DATALIST element as a standalone element and then bind it to an INPUT field through the new list attribute on INPUT fields. The net effect is that when the input field gets the focus the menu displays and the user can either enter free text or pick up one of the predefined options.

The problem with DATALIST is the same as with many other nice HTML5 features: the lack of extended and uniform support across browsers. DATALIST is fully supported in a good number of recent Chrome and Firefox builds; it is also supported in Internet Explorer 10. The actual user interface you get though is slightly different across all these browsers.

At any rate, the DATALIST element is characterized by a fixed list of options that require script code to be dynamically defined. In addition, the DATALIST is intended only to provide a list of likely values; the design is not intended for the purpose of restricting the possible values of the text box to be one of the listed values.

When it comes to enter country names, we want a keyboard-based solution that has the following characteristics:

  • It restricts the number of suggestions as the user types in the text box
  • It possibly gets suggestions from a remote service
  • It separates the displayed text from those codes that uniquely identify typed text (i.e., United States is the displayed text, but what you collect is the code of the nation, say, US)
  • It doesn’t accept anything other than the suggested values

How would you code this?

The only solution I can think of is a jQuery plugin. The popular jQuery UI library offers an autocomplete widget that works very well. I’ve used it effectively in production applications to select members of sporting clubs by name. Because the list of members could easily reach 1,000 names, the development team ruled out drop-down lists and set a HTTP web service to serve the list relying on jQuery native caching to reduce roundtrips. It worked beautifully, but it took me a while to fully configure the widget to support all the requirements listed above.

Bootstrap Typeahead Component

Recently, I was involved in a project that required auto-completion in some input forms. The team was already using Twitter Bootstrap for the HTML layout, so we decided to take a look at the Bootstrap Typeahead component for auto-completion instead of adding yet another (big) chunk of JavaScript to the download. Functionally speaking, Typeahead is the Bootstrap’s counterpart of jQuery UI autocomplete plugin.

Note: Twitter Bootstrap is based on jQuery anyway, and all of its more advanced features such as type-ahead are implemented on top of selected official jQuery plugins.

Let’s arrange a sample ASP.NET MVC application and reference Bootstrap scripts and stylesheets (both bootstrap.css and bootstrap-responsive.css).

<html> 
   <head> 
      <title>@Model.Title</title> 
      @Styles.Render("~/css") 
      <script src="@Url.Content("~/Content/Scripts/jquery-1.9.1.min.js")" 
         type="text/javascript"></script> 
      <script src="@Url.Content("~/Content/Scripts/bootstrap.min.js")" 
         type="text/javascript"></script> 
   </head> 
   :
</html>

The Typeahead component can be configured in pure markup if you have a static list of suggestions:

<input id="country" type="text" 
   data-provide="typeahead" 
   data-source="['one', 'two', ...]" />

If this works for you, then you’re all set and need no other code anywhere. This is not much different from having a DATALIST in place but at least it ensures cross-browser functionality.

If you want to use remote data source, then you need to add some script code. You can choose to bind the initialization code with the ready event of the host page or, more simply, as a page-specific script block placed at the bottom of the page. Here’s the markup.

<div class="container"> 
   <h2>Where would like to go on vacation this summer?</h2> 
   <div class="input-append"> 
      <input id="country" type="text" /> 
      <button class="btn">Enter</button> 
   </div>
</div>

And here’s the required initialization script:

<script type="text/javascript">
$(function () { 
   $('#country').typeahead({ 
      source: function (query, process) { 
         var url = ...; 
         var hints = []; 
         $.get(url, { term: query }, function (data) { 
            $.each(data, function (i, hint) { 
               hints.push(hint.value); 
               return process(hints); 
            }); 
         }); 
      } 
   });
</script>

The Typeahead plugin accepts some input options, the most important of which is the source function. The simplest implementation ever for source is below:

source: function (query) { 
   var hints = ["one", "two", ...];
   return hints;
}

This implementation is equivalent to using plain markup as shown earlier. If you intend to invoke a remote data source, then you should add a second parameter to the callback—the process parameter. The process parameter is an internal Bootstrap function that sets up the selection lifecycle in an async context such as when you get suggestions from a remote endpoint.

Whatever code you use to connect to the endpoint, for example, the $.get function, at some point you’ll need to package your raw response into an array of strings. Typeahead, in fact, requires that the source function returns an array of strings. In case of async download, pay attention to passing the list of hints to be displayed through the process function.

return process(hints);

It is also your responsibility to create the array of strings in the body of the function source. You create this array from the output received from the endpoint; if the endpoint already returns an array of strings, then you’re all set. However, in a realistic scenario you want to receive at least display text and unique ID from the server. This makes it necessary for you to copy in a local JavaScript array the display text. In the code shown above, the array of hints to be displayed is the variable hints.

The format of the URL depends on the HTTP service. In the example above, I’m assuming that the URL expects to receive the substring the user typed through a parameter named term. Here’s an ASP.NET MVC controller method that may provide hints on countries:

public JsonResult Countries(String term, Int32? maxRows)
{ 
   :
}

The for-each loop in the source method is also based on the known structure of the data returned by the HTTP endpoint. For the purposes of the demo, I’m using the following service from one of my sample sites:

http://www.expoware.org/caspio/hint/countries

The service gets the value of the term parameter and returns a list of matching countries packed in the following data structure:

public class AutoCompleteItem 
{ 
   public String label { get; set; } 
   public String id { get; set; } 
   public String value { get; set; }
}

In Bootstrap Typeahead you are completely free to name the query variable (i.e., term) as you prefer. Similarly, in Bootstrap Typeahead you are free to define the name and structure of the AutoCompleteItem class. The class above, though, allows you to share the same auto-complete class with jQuery UI where restrictions exist on the parameter name—which must be term—and the auto-complete class which must expose public methods such as label and value—typed lowercase. In general, label refers to the text being displayed in the auto-completion drop-down list whereas value is the text displayed in the text box once a selection is made. The two sometimes coincide; even though label may sometimes contain more information to help in the selection. (See figure.)

Assigning Each Selection a Unique ID

Let’s go back to the country example and see what it takes to select a country and, regardless of the displayed name, get the unique ID of the selected country. This requires adding a hidden field that will be updated each time the user makes a selection.

<input id="country-id" type="hidden" />

To handle the selection of an item in the drop-down list, you add the updater function, as below:

var items = {count: 0};
$('#country').typeahead({ 
   source: function (query, process) { 
      var url = ...; 
      var hints = []; 
      items = { count: 0 }; 
      $.get(url, { term: query }, function (data) { 
         if (data.length == 0) { 
            $("#country-id").val(""); 
            $("#country").val(""); 
            return; 
         } 
         $.each(data, function (i, hint) { 
            hints.push(hint.value); 
            items[hint.value] = hint; 
            items.count++; 
            return process(hints); 
         }); 
      }); 
   }, 
   updater: function (item) { 
      var currentItem = items[item]; 
      if (currentItem != null) { 
         $("#country-id").val(currentItem.id); 
         return currentItem.value; 
      } 
      return this.hide(); 
   }
});

The parameter item is the string that the user selected in the drop-down list. The problem now is in retrieving the original object that downloaded and that contained the displayed string in one of its properties. The Typeahead plugin is not brilliant here as it requires you to create manually an array of downloaded objects using the displayed string as the key. This array in the example is items.

Note also that you should clear the text boxes (including the hidden field) if the list of hints is empty. This ensures that no invalid argument is ever typed the text box.

To get the unique ID of the selected country you simply read programmatically the content of the hidden field. A common naming convention is naming the hidden field with a –id suffix such country-id if country is the name of type-ahead text box.

The above code ensures that, if the user edits the content of the text box after a selection and no country name is matched, then the code in the updater automatically clears out the text box.

jQuery UI vs Typeahead

No matter whether you call it type-ahead or auto-completion, is a good practice to suggest text to the users when they enter information into input forms. Both jQuery UI and Twitter Bootstrap provide for that. Which option should you choose? Needless to say, if you’re already using either framework for other pieces of UI then stick to that. If not, I prefer Typeahead because it’s overall simpler to operate; jQuery UI, however, offers more methods and control. Overall, common input scenarios are in my opinion cleaner to code with Typeahead. Don’t expect huge differences though and … feel free to disagree! At any rate, for a realistic application of auto-completion like the one depicted here, you need quite a bit of JavaScript code either way.

Dino Esposito

Author profile:

A long-time trainer and consultant, Dino is the author of many popular books for Microsoft Press for .NET developers.including “Architecting Mobile Solutions for the Enterprise“ and “Programming ASP.NET MVC” both for Microsoft Press. CTO of Crionet, a firm specializing in Web-based and mobile solutions for sport events across Europe (http://www.crionet.com), at the moment Dino is also technical evangelist for JetBrains, where he focuses on Android and Kotlin development, and member of the team that manages WURFL—the database of mobile devices used by organizations such as Google and Facebook. Follow Dino through his blog at http://software2cents.wordpress.com or at http://twitter.com/despos

Search for other articles by Dino Esposito

Rate this article:   Avg rating: from a total of 13 votes.


Poor

OK

Good

Great

Must read
Have Your Say
Do you have an opinion on this article? Then add your comment below:
You must be logged in to post to this forum

Click here to log in.
 

Top Rated

Acceptance Testing with FitNesse: Multiplicities and Comparisons
 FitNesse is one of the most popular tools for unit testing since it is designed with a Wiki-style... Read more...

Acceptance Testing with FitNesse: Symbols, Variables and Code-behind Styles
 Although FitNesse can be used as a generic automated testing tool for both applications and databases,... Read more...

Acceptance Testing with FitNesse: Documentation and Infrastructure
 FitNesse is a popular general-purpose wiki-based framework for writing acceptance tests for software... Read more...

TortoiseSVN and Subversion Cookbook Part 11: Subversion and Oracle
 It is only recently that the tools have existed to make source-control easy for database developers.... Read more...

TortoiseSVN and Subversion Cookbook Part 10: Extending the reach of Subversion
 Subversion provides a good way of source-controlling a database, but many operations are best done from... Read more...

Most Viewed

A Complete URL Rewriting Solution for ASP.NET 2.0
 Ever wondered whether it's possible to create neater URLS, free of bulky Query String parameters?... Read more...

Visual Studio Setup - projects and custom actions
 This article describes the kinds of custom actions that can be used in your Visual Studio setup project. Read more...

.NET Application Architecture: the Data Access Layer
 Find out how to design a robust data access layer for your .NET applications. Read more...

Calling Cross Domain Web Services in AJAX
 The latest craze for mashups involves making cross-domain calls to Web Services from APIs made publicly... Read more...

Web Parts in ASP.NET 2.0
 Most Web Parts implementations allow users to create a single portal page where they can personalize... Read more...

Why Join

Over 400,000 Microsoft professionals subscribe to the Simple-Talk technical journal. Join today, it's fast, simple, free and secure.