Auto-Complete Field with jQuery - Code Explained

Hello! As I promised, I’ll be explaining all the JavaScript code of the auto-complete field in this post. If you haven’t read the previous post where I talk about the auto-complete field take a moment to read it first before continuing.

Now take some cookies because the post is quite long. Be comfortable and let’s start!

First lets define how our auto-complete field should work. We want an input field that will present suggestions obtained by Ajax in a box below it when we type some text. Then we should be able to navigate through the suggestions by using the UP and DOWN arrow keys and select one by pressing ENTER/RETURN. Additionally we should be able to select a suggestion by clicking on it with the mouse. After selecting a suggestion, the text of the input field should be updated and the suggestion box should disappear. But if we hit the ESC key instead, the suggestion box should disappear without updating the value of the input field.

Now we can start coding. But before we define some global variables. We’ll discuss about them at the appropriated time.

var acListTotal   =  0;
var acListCurrent = -1;
var acDelay       = 500;
var acURL         = null;
var acSearchId	  = null;
var acResultsId	  = null;
var acSearchField = null;
var acResultsDiv  = null;

Now we define the function that is responsible for configuring the auto-complete field, setAutoComplete. This function expects 3 parameters: the id of the input field, the id of the results div and the URL of the remote script.

function setAutoComplete(field_id, results_id, get_url){

Here we initialize some variables. We concatenate the ‘#’ char with the id of the field to build the id string.

	// initialize vars
	acSearchId  = "#" + field_id;
	acResultsId = "#" + results_id;
	acURL 		= get_url;

Now we automatically create the div that will hold the returned data (the suggestions). We use the jQuery append() method to append the HTML code to the body tag. If you are just learning jQuery $(”body”) is the way to select the body element (or any other element) and play with it.

	// create the results div
	$("body").append('<div id="' + results_id + '"></div>');

Now we use jQuery again to select the elements to use later in the script.

	// register mostly used vars (jQuery object)
	acSearchField	= $(acSearchId);
	acResultsDiv	= $(acResultsId);

After creating the results div we now reposition it.

	// reposition div
	repositionResultsDiv();

Time to define some listeners now. First we add a blur() listener to the input field. This event will be triggered when the field loses its focus. It loses its focus when we click anywhere outside the field. For the event we define an anonymous function that uses setTimeout to call clearAutoComplete with a delay of 200ms. The clearAutoComplete function, as we will see, hides the suggestion box. The purpose of the delay is because we want the user to be able to click on a suggestion and only then hide the suggestion box. Without the delay a click on a suggestion would trigger the blur event hiding the box before the click event. So we set a delay long enough for catching the click before hiding.

	// on blur listener
	acSearchField.blur(function(){ setTimeout("clearAutoComplete()", 200) });

Now we add the keyup() listener to the field. This event is triggered when some key is pressed and released. Here is the place to check which key was pressed and act accordingly.

	// on key up listener
	acSearchField.keyup(function (e) {

To get the code of the key we use the keyCode property of the event object passed as the argument or window.event for Internet Explorer. We also store the present value of the input field in the lastVal variable using the val() method. The reason why we store the present value will be explained ahead.

		// get keyCode (window.event is for IE)
		var keyCode = e.keyCode || window.event.keyCode;
		var lastVal = acSearchField.val();

Now we call a function to check if the user pressed the UP or the DOWN arrow key so we can create the selection functionality. We put the code in a function just to be more organized. The function returns TRUE if one of the keys was pressed and then we can return.

		// check an treat up and down arrows
		if(updownArrow(keyCode)){
			return;
		}

If the pressed key was neither an UP nor a DOWN we check if it was an ENTER or an ESC key. If true we call the clearAutoComplete function an return.

		// check for an ENTER or ESC
		if(keyCode == 13 || keyCode == 27){
			clearAutoComplete();
			return;
		}

Finally if no “action” key was pressed we call the autoComplete function with a delay passing the present value of the input as a parameter. Now you might ask why we don’t simply call autoComplete without delay. We could, but that would generate an unnecessary overhead in the server because we would call the remote script for each pressed key and it’s not optimal. If the user already have an idea of what he wants and types some text quickly we should only check the complete text and not each step until there. So we add a delay before calling the autoComplete function and inside the function we will check if the text is the same as when it was called acDelay ms ago. If they are the same the user stopped typing and we can call the remote script.

		// if is text, call with delay
		setTimeout(function () {autoComplete(lastVal)}, acDelay);
	});
}

Let’s go through the autoComplete function now. This function is responsible for calling the remote script, processing the returned data and adding it to the suggestion box.

// treat the auto-complete action
function autoComplete(lastValue)
{

We get the current value of the field and make some validation. If it’s empty we call clearAutoComplete and return because there is nothing to suggest.

	// get the field value
	var part = acSearchField.val();
 
	// if it's empty clear the resuts box and return
	if(part == ''){
		clearAutoComplete();
		return;
	}

As explained before, this function is called with a delay so it only gets activated when the user stops typing. To check that we compare the current value of the input field with the value it had when this function was called acDelay ms ago. If both are equal the user stopped typing and we continue, but if they are different we return stopping the request.

	// if it's equal the value from the time of the call, allow
	if(lastValue != part){
		return;
	}

Now we use the getJSON function to make a request to the remote script that will return data as JSON. This jQuery function then passes us the data as native JavaScript type. The getJSON function takes an URL and a function to process the response as its parameters.

	// get remote data as JSON
	$.getJSON(acURL + part, function(json){

Our remote script returns an array of data, so we check its length to discover how many results we have. If there are results we populate the suggestion box, if not, we call clearAutoComplete to hide the box. The total of results is stored in the global variable acListTotal to be used later.

		// get the total of results
		var ansLength = acListTotal = json.length;
 
		// if there are results populate the results div
		if(ansLength > 0){
 
			var newData = '';

For each result we create a div pair containing the suggestion value and the class unselected.

			// create a div for each result
			for(i=0; i < ansLength; i++) {
				newData += '<div class="unselected">' + json[i] + '</div>';
			}

Now we insert the generated HTML code inside the results div and display it by setting the CSS display property to block. It was previously set to none in our stylesheet file.

			// update the results div
			acResultsDiv.html(newData);
			acResultsDiv.css("display","block");

Here we register mouse events for each suggestion so we can style them on mouse over and select them by mouse clicking. First we get all suggestion divs by selecting the results div children $(acResultsId + ” > div”)

			// for all divs in results
			var divs = $(acResultsId + " > div");

Now we register an anonymous function for the the mouseover() event. Inside this function we set the className property of each div as unselected by running the jQuery each() method and then we set the className of the current div (the one with the mouse over it) as selected. This way the div under the mouse pointer gets a selected state and all others an unselected state.

			// on mouse over clean previous selected and set a new one
			divs.mouseover( function() {
				divs.each(function(){ this.className = "unselected"; });
				this.className = "selected";
			})

We also register an anonymous function for the click() event. In this function we set the value of the input field as the value of the first child node of the clicked element. The clicked element is a suggestion div and its child node is the suggestion text itself. It’s pure DOM.

			// on click copy the result text to the search field and hide
			divs.click( function() {
				acSearchField.val(this.childNodes[0].nodeValue);
				clearAutoComplete();
			});

And we call clearAutoComplete if there are no results.

		} else {
			clearAutoComplete();
		}
	});
}

The clearAutoComplete function is pretty simple because of jQuery. First we remove all the HTML code from inside the results div and then we change the CSS display property to none which hides the suggestion box.

// clear auto complete box
function clearAutoComplete()
{
	acResultsDiv.html('');
	acResultsDiv.css("display","none");
}

The function repositionResultsDiv is responsible for positioning the div that contains the results according to the position of the input field.

// reposition the results div accordingly to the search field
function repositionResultsDiv()
{

Here we get the field’s offset and then the top and left position. The offset() method is provided by the jQuery Dimensions plug-in.

	// get the field position
	var sf_pos    = acSearchField.offset();
	var sf_top    = sf_pos.top;
	var sf_left   = sf_pos.left;

This time we get the height and width of the input field using methods provided by the jQuery itself. The Dimensions plug-in extends these functions for the document and window element.

	// get the field size
	var sf_height = acSearchField.height();
	var sf_width  = acSearchField.width();

Now we define the CSS styles of the suggestion box in such a way it shows up right below the input field. You may need to tweak these values to get an optimal result. First we set its position to absolute. Then we set the left position the same as the input field minus 2px (tweak). The top position is the top of the input field plus its height plus a little gap of 5px. Finally we set the width the same as the input field minus 2px (tweak again).

	// apply the css styles - optimized for Firefox
	acResultsDiv.css("position","absolute");
	acResultsDiv.css("left", sf_left - 2);
	acResultsDiv.css("top", sf_top + sf_height + 5);
	acResultsDiv.css("width", sf_width - 2);
}

The last function updownArrow is the one that controls the behavior of the arrow keys. It receives the keyCode as an argument and check if it’s either the UP(38) or the DOWN(40) arrow key. It uses the global variable acListCurrent to track the index of the current selected suggestion in the results div. The index starts with 0. The acListCurrent is initialized with -1 in the beginning of the script.

// treat up and down key strokes defining the next selected element
function updownArrow(keyCode) {
	if(keyCode == 40 || keyCode == 38){

So if the UP key was pressed it first checks the current position. If it’s 0 (the first suggestion) or if it’s -1 (the initial value) it changes the index to the last element (acListTotal-1). For any other value it just decrements the value of acListCurrent.

		if(keyCode == 38){ // keyUp
			if(acListCurrent == 0 || acListCurrent == -1){
				acListCurrent = acListTotal-1;
			}else{
				acListCurrent--;
			}

If the DOWN key was pressed it first checks if the current index is the index of the last element. If so it changes the index to the first position. Otherwise it increments the current value, walking down the list.

		} else { // keyDown
			if(acListCurrent == acListTotal-1){
				acListCurrent = 0;
			}else {
				acListCurrent++;
			}
		}

After the index of the next element was defined, we need to set the styles accordingly. For each suggestion (each child of the results div) we call an anonymous function passing its index as the parameter.

		// loop through each result div applying the correct style
		acResultsDiv.children().each(function(i){

Then we check if the index of the element is the same as the acListCurrent value. If it is, we change the value of the input field to the value of the element and set the element class to selected. If it’s not, we set the element class to unselected.

			if(i == acListCurrent){
				acSearchField.val(this.childNodes[0].nodeValue);
				this.className = "selected";
			} else {
				this.className = "unselected";
			}
		});

Finally we return true to indicate that one of the arrows was pressed. If none of the arrows was pressed we reset the acListCurrent value to -1 and return false.

		return true;
	} else {
		// reset
		acListCurrent = -1;
		return false;
	}
}

Great! We finally reach the end! I hope it was useful somehow. This code can still be improved and I’ll improve it in the near future. In the mean time, if you have any doubt or any suggestion, please post a comment.

Files:
Auto-Complete (all files)
autocomplete.js

46 Responses to “Auto-Complete Field with jQuery - Code Explained”

  1. TaMeR Says:

    First I like to thank you for the script.

    I am trying to get this to work with codeigniter http://codeigniter.com
    But I can’t figure out how because you are using $_GET
    How hart would it be to change this to use $_POST?
    Even better to use the input field name in place of ‘part’

  2. fromvega Says:

    Hello TaMer. To use the input field name in place of ‘part’ is easy. You just need to change the URL when calling setAutoComplete as the following:

    setAutoComplete("searchField", "results", "autocomplete.php?searchField=");

    Now, to use POST method instead of GET you will need to change some code. Here is what you need to do:

    Change the line 84 of autocomplete.js where you read $.getJSON(acURL + part, function(json){ to this:

    84
    85
    86
    87
    88
    89
    
    	$.ajax({
    		type: "POST",
    		url: acURL,
    		data: acSearchField.attr("name") + "=" + part,
    		dataType: "json",
    		success: function(json){

    And then change the old line 121 where you read }); to }});

    This little change should be enough to send a POST using the name of the input field as parameter. But please be aware that GET is the standard for requesting data.

    If you all like the idea I can write a post exemplifying some methods of the jQuery Ajax library.

  3. Rene A. Says:

    Very nice script and demo.

    Is there any change you could help me, to make this working with more then one input-field per page?

  4. fromvega Says:

    Hello! I would be glad to help you. As I said I’m planning a new release where there will be no limit to the number of input fields. But I’m just a little busy with another project these days.

    If you can wait I’ll try to post the new release by the end of this week. If not, you can duplicate the code and rename the global variables and functions for each field.

    Thank you for the audience.

  5. cg Says:

    so, how about doing a lookup against a database?
    for example, what if the user did a search for a name like “a” and you wanted your db to return a list like:

    select id, name from table where name like ‘a%’

    so if they see a list like:
    albert
    allison
    etc, whatever names starting in “a” in your table

    they could click one of the names and hit submit and go along their way

  6. fromvega Says:

    It’s pretty straightforward. Assuming you didn’t change the example URL and that you are using PHP you will have the “a” letter available in $_GET[’part’] to use it to build the SQL statement and perform the database query.

    After you would need to loop through the results adding each returned word to an array, like $names[] = $row['name']. And at last you would need to echo the JSON representation of your array. This way the input field would be populated with your database results.

    Now, to submit the chosen value you just need to put the input field inside a form tag and to create a submit button for it.

    Let me know if you need further help!

  7. Suissa Says:

    Greate plugin dude!

    congratz form Brazil

  8. Samalah Says:

    This looks to be a really great update/change to the jquery autocomplete plugin. I tried using the plugin directly from bassistance.de but had no luck in querying the database as I’m a complete php / sql newbie. If it wasn’t too much effort could you please post a quick sample of what the php and sql would look like for grabbing a value from the database?

    Thanks for taking the time to create the script and write about it, it’s much appreciated!

  9. fromvega Says:

    Hello Samalah, there are many ways of querying a database. Assuming that you are using MySQL you could use the mysqli extension (http://php.net/mysqli) or the PDO extension (http://php.net/pdo).

    The PDO extension “provides a data-access abstraction layer, which means that, regardless of which database you’re using, you use the same functions to issue queries and fetch data”.

    At http://php.net you can find almost everything you need, including examples. I’m thinking about making another post with complete information about using the script with a database. But here I’ll give you just a snapshot of what you could use, then you should read the manual.

    First you need to connect to the database. Then you need to execute a query statement built with the value supplied by the user. Let’s say for example that the input field would require a person’s last name. After you get the “part” typed by the user you need to make a database query to retrieve all last names that have “part”. The sql statement would need to be similar to the following:

    SELECT last_name FROM names WHERE last_name LIKE '%part%'

    Where “part” needs to be replaced by the value in $_GET[’part’] if you are using the original script. The “%” is a character that matches any number of characters.

    After executing this query you need to loop through the results adding each result to an array that will be returned as JSON.

    Sorry not to be more specific at this time but I promisse that I’ll make another post soon about working with the database.

    Thank you.

  10. Samalah Says:

    Thanks for taking the time to reply, I really appreciate it! I’ll look forward to reading your further articles….

    Samalah

  11. nuxx Says:

    Excellent tutorial, currently tweaking the script to my needs… may I suggest the addition of autocomplete=”off” to the input field, in order to prevent the browser from overriding your script with its own autocomplete feature, as firefox for instance seems to do when you type a word, after the form earlier has been submitted with a value beginning with the initial letter of the word in question.

  12. fromvega Says:

    Hello nuxx, thanks for the suggestion! I’ll address this issue in the next release. I think you would like to know that I already have implemented a jQuery plug-in for this script, allowing it to be applied to an unlimited number of fields.

    It’s just that I would like to make a really good post, showing how to implement it to retrieve data from a database for instance. But now I’m busy with other projects. I just need to tune the plug-in a little bit before releasing it.

    Keep tuned!

  13. Gary Says:

    Hi, first of all, this is an awesome playground, very cool and neat ideas were implemented, hat’s off.

    I have a question: I’m a PHP developer and client-side scripting is not my strength at all. Is that possible without massive modifications to send more than one variable to the server-side script? It would be a neat feature if it’s possible, the first thing popped in my mind is a blog where you can search for a thing in a given time quantum.

    Any input would be appreciated.

  14. fromvega Says:

    Hello Gary, sorry for the delay! To send another variable the easiest way is to write it as a GET parameter directly in the remote script URL.

    Where it reads autocomplete.php?part= you can write something like the following to send a variable called “newvar”:

    setAutoComplete(”searchField”, “results”, “autocomplete.php?newvar=value&part=”);

    Remember that part= needs to be in the end. Any other type of customization would imply writing javascript code.

    Bye!

  15. Gary Says:

    Hello,

    No problem for the delay, i resolved the problem exactly like you mentioned it, ironically just after an hour I posted the message here: with server-side injected custom variable harvested from sessions and post data.

    Thanks anyway for the answer,
    And a very big thank you for the script.

    Gary

  16. tester Says:

    Hi,
    Thanks for the great script. I love it a lot! One small problem that I am experiencing is the editable element’s width and height don’t look very well, I tried to adjust the sf_height and sf_width by *2 (e.g:acSearchField.height()*2), however, it doesn’t seem to be working. I also tried to override the width and height in css by declaring !important. No luck as well. Any idea how I can enlarge the editable field width and height?

    Thanks!

  17. riyas Says:

    Hi,
    I need to implement the same autocomplete feature in java . My back end is Ms access. If possible can u Send the source code to my mail id

    I am an enginnering graduate and i need to implement this feature in my project. Plaese help me out

  18. fromvega Says:

    Hello riyas, if you read this post more carefully you will notice that there is a link for the source code. But, anyway, here is the link: http://fromvega.com/code/autocomplete/autocomplete.zip

  19. yasser Says:

    hi, in your code i change
    $colors = array(’hola’, ‘blue’, ‘brown’, ‘green’, ‘grey’,
    ‘gold’, ‘navy’, ‘orange’, ‘pink’, ’silver’,
    ‘violet’, ‘yellow’, ‘red’,'Baja California’);

    by:

    $result = mysql_query(”select colores from color ORDER BY colores ASC”,$link);
    while($row = mysql_fetch_array($result)){
    $colors[] = $row[’colores’];
    }

    and doesnt appear anything in the input to autocomplete.
    what im missing??

  20. Matt Says:

    Hi
    Great script - possibly the best one I’ve seen

    I am waiting patiently for the the update to use multiple fields

    any idea when you’ll have a chance to look at it?

    Thanks

    Matt

  21. fromvega Says:

    Hello yasser, maybe there is something wrong in your PHP code and the returned data is an error string or maybe a warning. For it to work, the output needs to be a plain json string.

  22. fromvega Says:

    Thanks Matt, I created the multiple fields plugin already. But it’s lost somewhere in my hard drive. I have been busy with other projects. And I usually just get back to the site when there are comments! ;) But I’m going to try to find it and post it here as soon as possible!

  23. Tyler Rick Says:

    Thanks for the great work, fromvega. I’m glad you finally made a multiple-fields-capable plugin. That’s just what I need right now. Hopefully you will post it soon… In the meantime, looks like I’ll have to use

    http://bassistance.de/jquery-plugins/jquery-plugin-autocomplete/

  24. WoL Says:

    Yes multiple fields is all we need and this script will be king :D

  25. Macpaul Says:

    As a possible stop gap, I found a solution for multiple fields. As it stands if you have multiple setAutoComplete blocks on a page, the last one is the only one that applies to all your fields.

    I took the route of placing this code inside the ‘onfocus=’ event for each input. Seems to work quite well so far, with limited testing… :)

  26. thompsonson Says:

    Hey fromvega,

    Great script, nice and easy to understand, would like to get my hands on the multiple version if possible? even if it’s just the code without a post, that’d be brilliant.

    Please post it! :)

    Thanks for the good work dude,
    Matt

  27. fromvega Says:

    thompsonson, I will try to post the script soon as it’s already done but I’m not going to have much time to correct possible bugs ;)

  28. ganesh Says:

    i want to how to convert this into JSP. i just need to tweak $colors[] into a ArrayList ? Then how to proceed ! Please give me direction!

  29. Loo Says:

    Hi, for anyone who wants to call their array from a database, you need to do the following:
    $colors = array();
    $query = mysql_query(”SELECT * FROM tablename”) or die (mysql_error());
    while ($q = mysql_fetch_array($query)) {
    $colors = $q[”color”];
    }

    This code works really well, I’m using it for a postcode search, but it does tend to slow when there are a lot of results, so I also added:
    $part = $_GET[’part’];
    $length = strlen($part);
    // check the parameter
    if(isset($_GET[’part’]) and $_GET[’part’] != ” and $length > 3)
    {
    Otherwise the script can upset the browser. I am using it to search 56,000 entries, hence the $length stopping the results until the user has entered more then 3 characters.

    Hope this helps someone

  30. alp Says:

    thanks a lot

  31. Andrew Heiss Says:

    Any update on making it work with multiple fields?

  32. Michael Dale Says:

    Hi fromvega,

    Great script. Any news when the multiple version will be ready?

    Thanks!

  33. fromvega Says:

    Hello Andrew and Michael, I wrote the multiple version some time ago. But unfortunatly it was in an old hard disk that got corrupted. I’m going to try to recover its data by the end of the week.

  34. Paulo Says:

    hello fromvega, great job, very helpfull to me. Paulo

  35. sithot28 Says:

    Hi fromvega,
    great plugin, but it won’t working when element inside another , for example element inside jquery.layout (http://www.fabrizioballiano.net)

  36. Thilina Says:

    Hi fromvega,

    grate tutorial.thanks a lot. But I downloaded the sample code. But they didn’t work. I can figure out why is that. Please help. Thanks

  37. Zoran Says:

    Hello, great script u have made. But, can u make changes in script to highlight searched term in result eg. or in orange.

    Regards!

  38. Linda Says:

    I really appreciate this tutorial, because everything I had read was using mysql, and we use informix se. With your help, I was able to get my output to match yours and from that to get autocomplete to work with our codeigniter application. Thanks!

  39. Shweta Says:

    Hi fromvega,

    U have put great script. But can u do favor in some other way. I was in search of same script and found it from u but 1 extra and different requirement is,

    Here in your case if I type “r” in textbox red is displayed as a autosuggest list.

    I want is if I type “r” in textbox and then click on submit button then red should come in same way as autocomplete you are showing. here extra thing is submit (click event). not sure how can i implement with JSON in your code.

    Thanks in advance. If u can help it would be great.

  40. PADO Says:

    Great script, easy to use and implement into site.

    Thanks !!

  41. Arun Says:

    Nice, very useful script. ;)

  42. AbdelRahman Awad Says:

    Hi thanks alot for all your efforts

    i made a new auto complete plugin for auto complete which is very simillar to the Facebook search check it out

    http://blog.halwsa.net/2010/jquery-autocomplete-search/

    Cheers

  43. Elis Says:

    Hello, Thanks, excellent article, could you show me an example of returning JSON PHP file? I’m trying to implement it with ASP.NET, sorry but my English is pathetic …

  44. Obaid Says:

    Hi,

    Its not working in IE7. in FF it works perfectly.

    Please suggest

    Thanks
    Obaid…

  45. Rumaan Says:

    Great work, Thanks :)

  46. TheDocTor Says:

    Hi fromvega

    Good script I mus say..
    For those who stil looking out for multiple inputs compatibility, Use the jquery focus() function and put this code within that
    Ex: $(’#search’).focus(function(){
    setAutoComplete(”search”, “results”, “autocomplete.php?part=”);
    });

    This works sumwat!! Thanks to Macpaul (July 4th, 2008 at 10:24 am) :P
    Cheers!!

Leave a Reply