Today we are going to create AJAX search suggestions inside input text fields. This is different from AJAX Auto-suggest or AJAX Live Search. The idea is simple — we are going to show the user suggestive terms as (s)he types. When the user hits the enter key, the suggestion is sent as the query. This feature was there in Google's search-box before they launched Google Instant search. (I recently added this nice feature on PulsePro's Admin Panel.) Let's build it together!
Search Suggest Screenshot

What we need

All we need is PHP 5 and a simple database table to search. For the purpose of this tutorial, let us create table called world. You can find the SQL for the table here at MySQL's site or download the source files of the tutorial. (If you're super beginner with MySQL, here's how you can set this up.) Note that the database is pretty big so it may take some time to execute the SQL commands.
There are three tables inside the world database. We only need the city table for this tutorial. The structure is like the following:
We are going to let the users search the city names. So, the Name field is our hero!

CSS Files and Structure

By the time we end this tutorial, the directory structure would be like this:
In index.php file, we add the following lines:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>AJAX Search Suggest in Textfield</title>
<script type='text/javascript' src='http://code.jquery.com/jquery-1.6.2.min.js'></script>
<script type='text/javascript' src='searchSuggest.js'></script>
<link rel='stylesheet' href='style.css'></link>
</head>
<body>
<div id='wrapper'>
<form action="http://google.com/" method='get'>
	<div class="infoWrapper">
		<div class="infoTitle">Search Cities</div>
		<div class="infoContent">
			<input type="text" id="redText" disabled="disabled" autocomplete="off" value="" class='text long'/>
			<input type="text" autocomplete="off" id="search" value="" name="q" class='text long search'/>
		</div>
	</div>
	<div class="infoWrapper">
		<div class="infoTitle"> </div>
		<div class="infoContent">
			<input type="submit" value='Google Search' id='search_button' />
		</div>
	</div>
</form>
</div>
</body>
</html>
This is fairly simple file. The only thing that needs attention is the presence of a disabled input element. We are going to place this disabled input field behind the text input where the user actually types. Also note that we have turned off autocomplete. This is to prevent any unwanted text suggestions that the browser may show.
But how on earth could we possibly put the disabled text field behind active text input? Well, the following CSS (inside style.css) will do the magic.
html, body, div, h1, h2, h3, h4, h5, h6, ul, ol, dl, li, dt, dd, p, blockquote,
pre, form, fieldset, table, th, td { margin: 0; padding: 0; }

body {
font-size: 14px;
line-height:1.3em;
text-align:center;
font-family:Arial, sans-serif;
}

a, a:visited {
outline:none;
}

.clear {
clear:both;
}

#wrapper {
width:960px;
margin:0 auto;
text-align:left;
}

/**
Generic Form styles
**/
.infoWrapper {
	clear:both;
	margin-bottom:10px;
}

.infoTitle {
	color:#808080;
	float:left;
	width:230px;
	text-align:right;
	margin-right:5px;
	line-height:29px;
}

.infoContent {
	padding-left:130px;
	text-align: left;
}

.text{
    border-color: #CCCCCC #C3C3C3 #DDDDDD;
    border-style: solid;
    border-width: 1px;
    font-size: 14px;
    margin-right: 4px;
    padding: 5px;
}

.text.long {
	width:350px;
}

/**
Form Elements
**/
#redText, #search {
	background: none repeat scroll 0 0 transparent;
	padding-left: 5px;
	padding-right:17px;
	position: absolute;
	z-index: 100;
	text-transform: lowercase;
	line-height:1.3em;
}

#redText {
	z-index:99;
	color:red;
}

#search_button {
	margin-top:10px;
}
The first few lines (up to line 23) are to reset browser specific styles. (It's a good practice to use these styles in almost all HTML files.) Then, there are some generic form styles to prettify our form. What needs attention is here:
#redText, #search {
	background: transparent;
	padding-left: 5px;
	padding-right:17px;
	position: absolute;
	z-index: 100;
	text-transform: lowercase;
	line-height:1.3em;
}

#redText {
	z-index:99;
	color:red;
}
redText is the id of the disabled input and search is the id of active input field. We have positioned both absolutely. This makes one overlap on the other. Next, we set the z-index of disabled input less than the active input. This brings the active input to the front. The other property to notice is that we are forcing the user to type in lowercase (text-transform: lowercase). It's not a good practice but this makes things easier for us developers (at least for this tutorial).

Server Side Data Handling

Now, we are going to work with ajax.php.
<?php
//connect to mysql and select db
mysql_connect('YOUR_HOSTNAME', 'YOUR_USERNAME', 'YOUR_PASSWORD') or die("Cannot connect to database!");
mysql_select_db('world') or die("Cannot select database!");

$result = array();
$search_text = urldecode(filter_var(trim($_POST['q']))); // $_POST['q'] is the search string which user types. It is sent via ajax
$q = "SELECT * FROM City WHERE `Name` LIKE '{$search_text}%'";
$r = mysql_query($q);
if(mysql_num_rows($r)>0){
	$row = mysql_fetch_assoc($r);
	$result['suggestion'] = $row['Name'];
}
echo json_encode($result);
?>
	
Here we first connect to MySQL and select the world database. (Make sure you change YOUR_HOSTNAME, YOUR_USERNAME and YOUR_PASSWORD with proper credentials.) Then we create an empty array $result. We also santize the search string with filter_var. Next we query the City table with:
SELECT * FROM City WHERE `Name` LIKE '{$search_text}%'
This SQL statement finds the rows whose Name field starts with the search string. In case of several matches, we just need the first match. So we find the first match and set the suggestion with:
	$row = mysql_fetch_assoc($r);
	$result['suggestion'] = $row['Name'];
Finally, we encode the array as a JSON output.

JavaScript

Next we move to searchSuggest.js and put the following code inside it:
$(function(){
	$("#search").keyup(function(e){
		$this = $(this);
		$searchBtn = $('#search_button');
		search_text = $this.val();
		$('#redText').val('');
		if(search_text){
			$("#redText").css({"background":"#FFF url('images/spinner.gif') right center no-repeat"});
			$.ajax({
				url: "ajax.php",
				type: "post",
				dataType: "json",
				data: "q="+encodeURIComponent($this.val()),
				error: function(){
					// pass
				},
				success: function(obj){
					$("#redText").css({"background-image":"none"});
					if('suggestion' in obj) {
						if(obj.suggestion!=null){
							$('#redText').val(obj.suggestion);
							if(e.keyCode === 39){ // right arrow key pressed
								$('#redText').val('');
								$this.val(obj.suggestion);
							} else if(e.keyCode === 13) { // enter pressed
								$("form").submit();
							}
						}
					}
				}
			});
		}
	});
	
	$("form").submit(function(e){
		e.preventDefault();
		suggestion = $("#redText").val();
		if(suggestion!=''){
			q = suggestion;
			$("#search").val(suggestion);
		} else {
			q = $("#search").val();
		}
		location.href = "http://google.com/#q="+q;
	});
});
Lot of code. Let's break this down into smaller parts. First, when the user types into the text-field, we clear out the disabled text-field (#redText) where we are going to place the suggestions. Then, as the user types and as we make an ajax request, we show an animated loading image at the right side of the input field, with the following line:
$("#redText").css({"background":"#FFF url('images/spinner.gif') right center no-repeat"});
Then, we make ajax request to ajax.php, which we already created in the last step. Note that we didn't do anything if we fail to make ajax call (the error property of ajax call). This is because we don't want to interrupt the user while typing. When we receive ajax response, we first hide the animated background. Then, we check if there is at all any suggestion sent via ajax (i.e. if there is any City name matching the typed characters). The following codes are important here:
if('suggestion' in obj) {
	if(obj.suggestion!=null){
		$('#redText').val(obj.suggestion);
		if(e.keyCode === 39){ // right arrow key pressed
			$('#redText').val('');
			$this.val(obj.suggestion);
		} else if(e.keyCode === 13) { // enter pressed
			$("form").submit();
		}
	}
}
If suggestion exists, we set the value of the disabled input text (redText) to the suggestion. Next, we go a bit further and do something more. If the user presses the right arrow key, we set the value of the active text field to the suggestion. Again, if the user presses Enter key, we submit the form.
So, what happens if user submits the form?
$("form").submit(function(e){
		e.preventDefault();
		suggestion = $("#redText").val();
		if(suggestion!=''){
			q = suggestion;
			$("#search").val(suggestion);
		} else {
			q = $("#search").val();
		}
		location.href = "http://google.com/#q="+q;
	});
We first check if there is any suggestion. If there is, we set the value of the active text field to the suggestion and set the search query (q). If there is no suggestion, search query is the same as the text user typed in. Then, we take the user to the Google search page for the search query.
Of course, if your site is http://example.com and your search results page is http://example.com/search, the last step would be something like:
location.href = "http://example.com/search/?q="+q;
And that's all! Hope you enjoyed the tutorial. Don't forget to check out other tutorials and subscribe to the feed!
blog comments powered by Disqus