jQuery Autocomplete Dropdown

Introduction

Here we're going to be building a predictive/autofill drop down box that will let your users see suggestions based on what they're typing. This can be commonly seen on Facebook and Google when searching or tagging users. I suggest you take a look at the awesome JSON article that we have before starting. We'll also be using jQuery, which is a super powerful JavaScript library.

HTML & CSS

We need to start by getting some HTML written up to hold our search box, and results. We'll be showing our results in a simple unordered list, with each list item containing a span that has the name, and a span underneath with a little bit of a description.

Something along the lines of the following should work well:

<div class='suggest-holder'>  
    <input type='text' class='suggest-prompt'>
    <ul>
        <li>
            <span class='suggest-name'>Michael</span>
            <span class='suggest-description'>The Writer</span>
        </li>
        <li>
            <span class='suggest-name'>Ben</span>
            <span class='suggest-description'>The Other Writer</span>
        </li>
        <li>
            <span class='suggest-name'>Ben</span>
            <span class='suggest-description'>The CodeIgniter Writer</span>
        </li>
    </ul>
</div>

Of course, that is all well and good, but at the moment it looks dreadful, let's give it some very basic styling with CSS, I've just used the following:

.suggest-holder {
    width: 150px;
}
    .suggest-holder input {
        width: 146px;
        border: 1px solid rgba(0,120,0,.6);
    }
    .suggest-holder ul {
        display: none;
        list-style: none;
        margin: 0;
        padding: 0;
        border: 1px solid rgba(0,120,0,.6);
        margin-top: -6px;
    }
        .suggest-holder li {
            padding: 5px;
        }
        .suggest-holder li:hover {
            cursor: pointer;
        }
        .suggest-holder li:hover, li.active {
            background: rgba(0,120,0, .2);
        }
            .suggest-name {
                font-weight: bold;
                display: block;
                height: 15px;
            }
            .suggest-description {
                font-style: italic;
                font-size: 11px;
                color: #999;
            }

This will just give us a nice section that we can use, at the moment the whole drop down will be visible, this is not what we want, so go ahead and drop a display: none; on the ul that we have. Also, we don't need the li elements that we have within the ul, but just remember the markup so that we can use them later to inject results with JS.

The JS

There are a few parts to this:

  1. The array storing data
  2. Keyup event on the input
  3. Selection of the item
  4. Show selected items

Array storing data

We're going to just store the items in a array of objects so that we can then traverse them to return the results that we want, for this we don't need a complex array, something simple like:

var data = [
    {
        name: 'Michael',
        description: 'The Writer'
    },
    {
        name: 'Ben',
        description: 'The Other Writer'
    },
    {
        name: 'Joel',
        description: 'The CodeIgniter Writer'
    }
];

Now that we have the array of objects, let's look into taking the user's key presses...

Keyup event on the input

For this we're going to use the jQuery .on() method which let's us define a few different events to listen to on an element, for now we will listen to the keyup method.

$('.suggest-holder input').on({
    keyup: function(e){

    }
});

Select Item

We select the items to show within the keyup event, for this we're going to run some very basic logic:

The code below should do that for us:

// Clear the ul
$('.suggest-holder ul').empty();

// Cache the search term
$search = $(this).val();

// Search regular expression
$search = new RegExp($search.replace(/[^0-9a-z_]/i), 'i');

// Loop through the array
for(var i in data){
    if(data[i].name.match($search)){
        $('.suggest-holder ul').append($("<li><span class='suggest-name'>" + data[i].name + "</span><span class='suggest-description'>" + data[i].description + "</span></li>"));
    }
}

That will then append all matched items to the holder ul, note here that we're removing any letter is that not alphanumeric or underscore from the search before matching with items in the object.

We need to use the below line to show the list of suggestions:

// Show the ul
$('.suggest-holder ul').show();

Show the selected items

We need someway to show what items have been selected by the users, to do that we need to add an event listener to each li that will simply add the item to an element on the page:

For this, we're not going to do anything special, but we're going to simply add a new li for each thing that is selected to a ul - we'll just give it an id of selected-suggestions

$('.suggest-holder li').on('click', function(){
    $('#selected-suggestions').append($('<span>' + $(this).find('.suggest-name').html() + '</span>'));
});

Nothing too complex there, but enough to make it so that we can see what has been selected from the drop-down list that we show.

Basics Complete

This is a very crude system that we have in place, now we can push this a lot further to make it a completely awesome system for people to use, to do that we'll be pushing the keyup method of the input a lot further, and expanding the code there.

Pushing it further

The main slightly more advanced things that we want to add are:

Keyboard navigation

Here there are 2 different keys that we want to listen to:

For this we want to set a global integer that will store which number of item we're onto, and then when you press one of the keys it will alter this number and then set a class of active that we set the CSS for earlier to the correct li:

if(e.which == 38){
    // Down arrow
    index--;

    // Check that we've not tried to select before the first item
    if(index < 0){
        index = 0;
    }

    // Set a variable to show that we've done some keyboard navigation
    m = true;
}else if(e.which == 40){
    // Up arrow
    index++;

    // Check that index is not beyond the last item
    if(index > $('.suggest-holder li').length - 1){
        index = $('.suggest-holder li').length-1;
    }

    // Set a variable to show that we've done some keyboard navigation
    m = true;
}

// Check we've done keyboard navigation
if(m){
    // Remove the active class
    $('.suggest-holder li.active').removeClass('active');
    $('.suggest-holder li').eq(index).addClass('active');
} 

We will integrate this into the rest of the code at the very end.

Enter and Esc key support

This is pretty simple, we want to be able to make it so that users can do everything from the keyboard:

To do that, we'll again, use the keyup event and listen for the above keys using the following code:

if(e.which == 27){
    // Esc key
    $('.suggest-holder ul').hide();
}else if(e.which == 13){
    // Enter key
    if(index > -1){
        index = -1;
        $('#selected-suggestions').append($('<span>' + $('.suggest-holder li.active').find('.suggest-name').html() + '</span>'));
        $('.suggest-holder li.active').removeClass('active');
    }
}

Of course, what we might want to do here is actually create a function for adding items to the selected list, this should take the element as a variable and will tidy up some of the code. But we'll get to the refactored code at the end!

Extra thoughts

One thing you'll notice if you run the code now is that when using the up and down arrows, the cursor jumps to the start or end of the text entered. To fix that we listen to the keydown event and preventDefault() on the event for those keys:

if(e.which == 38 || e.which == 40 || e.which == 13){
    e.preventDefault();
}

Secondly, you'll want to add in some code that will hide the suggestions drop down when the user clicks anywhere other than on one of the suggestions or input box, to do that we want to attach a click event to the body element, which will check if you've clicked one of the li elements or the input - if not, hide:

$('body').on('click', function(e) {
    if (!$(e.target).closest('.suggest-holder li, .suggest-holder input').length) {
        $('.suggest-holder ul').hide();
    };
});

Finally, you'll want to make it so that when you click within the box with something already being entered in it, that it reshows the suggestions box, for this you will want to listen to the focus event and just show the ul.

Pushing Further

This is a very basic start on how to accomplish a basic suggestion box, but what you might want to do is have it so that when you click on one of the items it gets removed from the dropdown, with the option then for people to delete the selected suggestions. There are a few ways to do this:

The End

One major thing to remember is to refactor the code when you've finished it - as this has been written in sections, the code is going to be a lot less optimised than it could be. Take a look at the full complete version, including refactored code on our demo page.

Tag: jQuery