David Myers

jQuery UI Autocomplete

This is an archived blog post from 2012

One of the great features of jQueryUI is the Autocomplete function. It handles the process of making AJAX requests for the purpose of having a text input which has an autocomplete function. The easiest way to implement this feature using jQueryUI is to have a pre-defined array of values in your javascript. However, most of the time you want your autocomplete values to come from a database. For instance, if you've got a table full of users you might want to be able to select a specific user in a form of some type. This especially becomes an issue when you have more than 20-30 users in that table. Sadly enough, the documentation for the jQueryUI Autocomplete function is kind of lacking. But after extensive use and digging into the code, you realize how easy the autocomplete can be to use. We'll start by looking at our form:

<form>
    <input type="text" id="NameInput"/>
    <input type="hidden" id="HiddenUserID"/>
</form>

Next we'll take a look at the javascript involved:

$(document).ready(function(){
  $("#NameInput").autocomplete({
        source: 'requests.php', // The source of the AJAX results
        minLength: 3, // The minimum amount of characters that must be typed before the autocomplete is triggered
        focus: function( event, ui ) { // What happens when an autocomplete result is focused on
            $("#NameInput").val( ui.item.value );
            return false;
      },
      select: function ( event, ui ) { // What happens when an autocomplete result is selected
          $("#NameInput").val( ui.item.value );
          $('#HiddenUserID').val( ui.item.id );
      }
  });
});

This example is a fairly basic usage of the autocomplete function, but it's enough for most situations. The first two options are self explanatory and the second two options are what makes this autocomplete really shine. One thing to note about the source option is that you can include GET url parameters if you want, ie 'requests.php?action=autocomplete'. In most applications, users are referenced by unique id's. However, when selecting a user in a form, you don't want to be choosing from faceless numbers. This presents a problem with a standard autocomplete function because they usually pull results in as a delimited string or single dimension array. In those cases, you can't select a user by there name yet have the form see their unique id as the value. This is where our second two options come in.

Our source will be sending back a user id and a user name for each result. This way we can show the users name in the form but send the users id to the form processor. The focus option is currently setting the value of the input box to whichever result is focused upon. This way you can use your arrow keys to scroll through the results while only looking at the input box. The select option is currently setting the the input box value to the users name and a hidden input value to the users id.

One final thing to note is the code in the select option:

function ( event, ui ) {
    $("#NameInput").val( ui.item.value );
    $('#HiddenUserID').val( ui.item.id );
}

This code allows us to reference individual result objects which are sent back from our AJAX source. "ui.item" references the result object and "value" and "id" reference the object properties.

Now that the javascript is in place, we'll look at the PHP. Obviously not everyone uses PHP on their backend so at the end of the PHP code I'll show and describe exactly what the backend code needs to be returning to the javascript. Also, the backend code that I'll be using is pretty simple and transferable between languages.

<?php
if(isset($_GET['term']))
{
    $term = strtolower($_GET['term']); // The autocomplete function sends your search term as the GET parameter 'term'
    // This is a simple example of doing a search for a full name that is similar to what we typed
    $query = mysql_query("SELECT id, fullname FROM $table WHERE fullname LIKE '%$term%'");
    if($query && mysql_num_rows($query) != 0)
    {
        $results = array();
        for($x = 0; $x < mysql_num_rows($query); $x++)
        {
            $tempArray = array(
                'id' => mysql_result($query, $x, 'id'),
                'label' => mysql_result($query, $x, 'fullname'),
                'value' => mysql_result($query, $x, 'fullname')
            );
            $results[] = $tempArray;
        }
        echo json_encode($results);
    }
}
?>

The first thing to note is that our javascript is sending what we type into our input box as the GET parameter 'term'. Then we use that term to query our database. For each result/row that we get, we create an array. We then add each of these small arrays to our overall results array. Once we iterate through all of our results and create an overall result array, we encode it in json and print it to the screen. The two most important parts of our backend code are the query and the parsing of the results. The query is important because it directly affects our autocomplete results. The above code shows what that query would look like if you were only looking for an id and full name while searching on one column. You could easily select any number of columns and search on more than one as well. For example if you didn't have a full name column, but a first and last name columns. In that case you would need to determine whether or not the search included a space (denoting more than one name being typed) and then deciding how to use the search term to search your table. This is an example of how that could be implemented:

$term = $_GET['term']; // Get search string
$term = explode(' ', $term); // Split search string at spaces

if(count($term) < 2) // Check if there was more than one word/name typed
{   // If there was only one word/name typed
    $name = strtolower($term[0]); // Set our search term and make it lowercase
    $query = mysql_query("SELECT id, firstname, lastname FROM $table WHERE firstname LIKE '%$name%' OR lastname LIKE '%$name%'");
    if($query && mysql_num_rows() != 0)
    {
        $results = array();
        for($x = 0; $x < mysql_num_rows($query); $x++)
        {   // Create a full name using our first and last name results
            $fullname = ucwords(mysql_result($query, $x, 'firstname')) . ' ' . ucwords(mysql_result($query, $x, 'lastname'));
            $tempArray = array(
                'id' => mysql_result($query, $x, 'id'),
                'label' => $fullname,
                'value' => $fullname
            );
            $results[] = $tempArray;
        }
        echo json_encode($results);
    }
}
else
{   // Both a first and last name were entered
    $firstname = strtolower($term[0]); // Set our first name to lowercase
    $lastname = strtolower($term[1]); // Set our last name to lowercase
    // We need to select distinct results because we when we find our person, it will return a result for each parameter it matches; in this case that would give us two of the same result
    $query = mysql_query("SELECT DISTINCT id, firstname, lastname FROM $Database->tbl_User WHERE firstname LIKE '%$firstname%' OR lastname LIKE '%$lastname%'");
    if($query && mysql_num_rows() != 0)
    {
        $results = array();
        for($x = 0; $x < mysql_num_rows($query); $x++)
        {   // Create a full name using our first and last name results
            $fullname = ucwords(mysql_result($query, $x, 'firstname')) . ' ' . ucwords(mysql_result($query, $x, 'lastname'));
            $tempArray = array(
                'id' => mysql_result($query, $x, 'id'),
                'label' => $fullname,
                'value' => $fullname
            );
            $results[] = $tempArray;
        }
        echo json_encode($results);
    }
}

Performing the query across multiple columns does make things a little more complicated, but it's much easier than adding a full name column to your table if you don't already have one.

The next thing to look at is the way that we parse our results and return them to the javascript. The autocomplete function expects a JSON object to be output to the screen. In a simple example this JSON object would essentially be a comma delimited list of strings. However, we want more functionality so it will be a JSON object with filled with arrays (which are fairly analogous to objects in this context). We are inserting each result row that we receive from our query into an array. Each of these arrays are then added to an overall result array which is then encoded into a JSON object. If only given one result, our JSON object would look something like this:

[{"id":"1","label":"David Myers","value":"David Myers"}]

The last thing to note about our result parsing is the key names that we're using in our individual result arrays. The 'id' key is one of my own creation so that I can use it in the background of the HTML form. However, the 'label' and 'value' keys are jQuery UI specific and required. The 'label' array value is used to populate the drop-down list of possible matches when the autocomplete function is active. The 'value' array value is used to set the value of the HTML input element that our autocomplete function is attached to. There are some cases where you might want your 'label' and 'value' values to be different, like if you wanted to include more user information in the dropdown but only the user name in the input box, but we want them the same for our example. When creating your result arrays you can add as many keys and values as you want. Then you can access them in the javascript in the same way we're referencing the 'id' key, ie 'ui.item.id'.

If you now go back and look at the javascript, you can easily see how the JSON we're returning is being used. And once you have a firm grasp on how to create and use the JSON, you can start adding additional functionality. The focus and select options of the autocomplete function in addition to the ability to customize the results you're returning enable you to create amazing and powerful autocomplete actions in your page.

comments powered by Disqus