高手莫入,浅显例子而已。最近在更换项目中的javascript库,觉得如果能把实践的过程记录下来,应该可以帮助到一些对javascript感兴趣的前端初学者。

jquery-auto-complete

每天使用百度,google,有的时候,你的网站或许需要一个自动完成的功能。 你可以在这个例子中,搜索新浪,或者苏洋,因为是测试PHP,没有数据库请求操作,只有简单的两个词库,你懂的。

完整例子: http://thecdn.sinaapp.com/page/demo/jq-auto-complete/

首先还是页面结构

结构草图: http://thecdn.sinaapp.com/page/demo/jq-auto-complete/step1.html

<!doctype html>
<html>
<head>
	<meta charset="utf-8">
	<title>JQUERY AUTO COMPLETE</title>
	<link rel="stylesheet" href="extra/style.css">
	<script type="text/javascript" src="extra/jquery-1.8.3.min.js"></script>
	<script type="text/javascript" src="extra/jquery.auto-complete.js"></script>
</head>
<body>
	<div id="warp">
		<div id="logo"></div>
		<input type="text" id="autocomplete">
		<input type="submit" id="search" value="搜索" class="s_btn">
		<div id="result">
			<table id="wordlst" cellspacing="0" cellpadding="2">
				<tbody>
					<tr><td><span>新浪</span><b>微博</b></td></tr>
					<tr><td><span>新浪</span><b>微博登陆</b></td></tr>
					<tr><td><span>新浪</span><b>邮箱</b></td></tr>
					<tr><td><span>新浪</span><b>nba</b></td></tr>
					<tr><td><span>新浪</span><b>爱问</b></td></tr>
					<tr><td><span>新浪</span><b>体育</b></td></tr>
					<tr><td><span>新浪</span><b>博客</b></td></tr>
					<tr><td><span>新浪</span><b>爱问共享资料</b></td></tr>
					<tr><td><span>新浪</span><b>网</b></td></tr>
					<tr><td><span>新浪</span><b>邮箱登陆</b></td></tr>
				</tbody>
			</table>
		</div>
	</div>
</body>
</html>

然后是样式

样式: http://thecdn.sinaapp.com/page/demo/jq-auto-complete/step2.html

*,html,body,div,ul,li,h3,p{
	margin: 0;
	padding: 0;
}
body, input{
	font-size: 12px;
	font-family: arial,\5b8b\4f53,sans-serif;
}
body{
	background-color:#F7F7F7;
}
div,ul,li,h3,p{
	float: left;
	display: block;
}

div#warp{
	width: 460px;
	height: 200px;
	top: 50%;
	left: 50%;
	position: absolute;
	margin-left: -200px;
	border: 1px solid #E0E0E0;
	margin-top: -200px;
	background-color: #FEFEFE;
}
div#warp div#logo{
	background: url(logo.png) 0 0 no-repeat;
	width: 220px;
	height: 64px;
	margin: 20px 0 0 130px;
	position: absolute;
}

div#warp input[type=text]{
	display: block;
	float: left;
}
div#warp input[type=text]#autocomplete{
	width: 200px;
	height: 22px;
	padding: 4px 7px;
	font: 16px arial;
	border: 1px solid #CDCDCD;
	border-color: #9A9A9A #CDCDCD #CDCDCD #9A9A9A;
	vertical-align: top;
	outline: none;
	margin: 100px 0 0 110px;
	background-color: white;
}

/*这里JS处理*/
div#warp div#result{
	border: 1px solid #817F82;
	position: relative;
	margin: 0 0 0 110px;
	text-align: left;
	-webkit-user-select: none;
	float: left;
	clear: left;
	min-width: 216px;
	display: none;/*列表首先弄掉,有数据再显示*/
}
div#result table{
	width: 100%;
	background: white;
	cursor: pointer;
	border-collapse: collapse;
	border-spacing: 0;
}
div#result table td {
	color: black;
	font: 14px arial;
	height: 25px;
	line-height: 25px;
	padding: 0 8px;
	text-align: left;
}
div#result table tr.hover {
	background-color: #e2eaff
}
div#result table td b {
	color: black;
}

div#warp input[type=submit]#search{
	display: block;
	float: left;
	margin: 90px 0 0 5px;
	height: 32px;
	cursor: pointer;
	background: url(btn.png) 0 0 no-repeat;
	border: 0;
}
div#warp input[type=submit]#search{
	display: block;
	float: left;
	margin: 100px 0 0 5px;
	height: 32px;
	cursor: pointer;
	background: url(btn.png) 0 0 no-repeat;
	border: 0;
	padding: 0 14px;
	font-size: 14px;
	width: 53px;
}
div#warp input[type=submit]#search.clicked{
	background-position: -55px 0;
}

接着一边想需求,一边实现吧。

基本事件: http://thecdn.sinaapp.com/page/demo/jq-auto-complete/step3.html

//首先是搜索按钮
//搜索框样式设置以及按钮事件
var searchBtn = $('#search');
//鼠标按下
searchBtn.bind('mousedown',function(){
	$(this).addClass('clicked');
	//开始搜索
	//搜索代码...
});

//鼠标移出
//可以体会一下,如果不绑定这个事件
//(只是在鼠标按下的时候去掉样式)
//在按钮按下鼠标,并移出按钮
searchBtn.bind('mouseout',function(){
	$(this).removeClass('clicked');
});


//结果输出
//缓存所有的表格tr元素
var target = $('table#wordlst').find('tr');
//元素数量
var count = target.length;

//if (!count) {return;}

//获取要事件委托的容器
var warp = $('table#wordlst').find('tbody');
//当前有焦点的元素项
var hover = 'hover';
//当前选择的元素的序号
//默认0是无,范围1~元素数量
var index = 0;
//初始化搜索关键词
var keyword = '';

//显示候选词列表
var showWordList = function(arrow){
	var warp = $('div#result');
	if (!arrow) {
		//重置焦点为0
		index = 0;
		//去掉所有的焦点hover
		for(var i=0;i<count;i++){
			if ($(target[i]).hasClass(hover)) {
				$(target[i]).removeClass(hover);
			}
		}
	}

	//根据关键词选择是否展示下拉框
	if(keyword == ""){
		warp.hide();
	}else{
		warp.show();
	}
}


//绑定table>tbody的鼠标移动事件
warp.bind('mouseover', function(e){
	var cur = e.target;
	//去掉所有其他元素的class中的hover
	target.removeClass(hover);
	//给每个元素的父级最上层元素加hover
	$(cur).parentsUntil(warp).addClass(hover);
	//通过循环获取当前有焦点的项目的序号
	for(var i=0;i<count;i++){
		if ($(target[i]).hasClass(hover)) {
			index = i;
		}
	}
	e.stopPropagation();
});

//绑定table>tbody的鼠标点击事件
target.bind('click',function(e){
	var cur = e.target;
	target.removeClass(hover);
	//将选择的元素的内容赋值给关键词
	keyword = $(cur).parentsUntil(warp)[0].innerText;

	//调用搜索
	//搜索代码...

	event.stopPropagation();
});

//绑定搜索框事件
//鼠标按下,方向键上,方向键下,ESC,回车
$('#autocomplete').bind('keydown', function(e){
	//根据index数值来进行候选下拉菜单的高亮
	var userSelect = function(){
		for(var i=0;i<count;i++){
			if ($(target[i]).hasClass(hover)) {
				$(target[i]).removeClass(hover);
			}
		}
		$(target[index-1]).addClass(hover);
		//设置输入框内容为选择中的内容
		$('#autocomplete')[0].value = $(target[index-1])[0].innerText;
		//把选择的内容赋值给关键词
		keyword = $('#autocomplete')[0].value;
	}

	//设置光标到字符串最后
	var setPointEnd = function(){ 
		var text = document.getElementById("autocomplete"); 
		var len =  text.value.length;

		if (text.setSelectionRange){
			setTimeout(function(){
			text.setSelectionRange(len, len);
			text.focus();
		}, 0);
		}else if (text.createTextRange){
			var textArea=document.getElementById("autocomplete");
			var tempText=textArea.createTextRange();
			tempText.moveStart("character",tempText.text.length);
			tempText.select();
		}
	}

	switch(e.keyCode){
		//方向键上
		case 38:
			//如果当前选择的内容比第一项小
			//那么设置焦点为最后一项
			//否则就数值减一
			if (index <= 1) {
				index = count;
			} else {
				index--;
			}
			//调用函数userSelect
			//设置选中项目高亮
			//以及对关键词赋值
			userSelect();
			//设置光标到最后
			setPointEnd();
			//显示候选词
			showWordList(true);
			break;
		case 40:
			//如果当前选择的内容比最后一项大
			//那么设置焦点为第一项
			//否则就数值加一
			if (index > count-1) {
				index = 1;
			} else {
				index++;
			}
			//调用函数userSelect
			//设置选中项目高亮
			//以及对关键词赋值
			userSelect();
			//显示候选词
			showWordList(true);
			break;
		case 13:
			//回车触发搜索
			if (index==0) {
				//没有展示下拉选择框
				keyword = $('#autocomplete')[0].value;
			}else{
				//选择备选
				keyword = $(target[index-1])[0].innerText;
			}
			//刷新备选列表
			//刷新列表代码...
			//调用搜索
			//搜索代码...

			break
		case 27:
			//触发ESC
			//关闭下拉框
			//并把index设置为0
			index=0;
	}

	e.stopPropagation();
});


if($.browser.msie){
	$('#autocomplete').on('propertychange', function(){
		//动态赋值,匹配计算
		keyword = $('#autocomplete')[0].value;
		showWordList();
	});
}else{
	$('#autocomplete').on('input', function(){
		//动态复制,匹配计算
		keyword = $('#autocomplete')[0].value;
		showWordList();
	});
}

之前一直是用写死的table数据,实际使用中,我们需要从服务器后端获取数据,所以呢。

接下来开始优化之前的代码和进行数据绑定。 要交互数据,首先要设计数据格式。

{
	query:"新浪",
	result:["新浪微博",
			"新浪微博登陆",
			"新浪邮箱",
			"新浪nba",
			"新浪爱问",
			"新浪体育",
			"新浪博客",
			"新浪爱问共享资料",
			"新浪网",
			"新浪邮箱登陆"]
}

接下来用这份数据,生成我们需要的表格。 顺便把刚刚随便想到的草稿代码,整理一下

输出数据: http://thecdn.sinaapp.com/page/demo/jq-auto-complete/step4.html

var searchBtn = $('#search');
searchBtn.bind('mousedown',function(){
	$(this).addClass('clicked');
	//开始搜索
	//搜索代码...
});
searchBtn.bind('mouseout',function(){
	$(this).removeClass('clicked');
});


window.$SAE = [];
//给一份默认的热词列表,优化体验
window.$SAE['QUERY_WORD'] = 
{
	query:"新浪",
	result:[["新浪微博!","http://weibo.com/"],
			"新浪微博登陆!",
			"新浪邮箱!",
			"新浪nba",
			"新浪爱问",
			"新浪体育",
			"新浪博客",
			"新浪爱问共享资料",
			"新浪网",
			"新浪邮箱登陆"]
};

//自动完成列表
var autoWordList = $('table#wordlst');
//输入框
var autoWordText = $('#autocomplete');
//为输入框光标缓存一份
var textCursor = document.getElementById('autocomplete'); 

var warp = autoWordList.find('tbody');
var warpBox = autoWordList.parent();

var target = autoWordList.find('tr');
var count = target.length;

//搜索设置
var searchHost = 'http://www.baidu.com/s?wd=';
//JSON数据接口
var dataHost = 'http://localhost/query.php?';
var keyWord = '';
var sarchPrefix = '+site:sae.sina.com.cn';
var newWindows = true;
//输入框限制搜索内容长度
var keyWordMaxLen = 100 -1;

//延时执行的句柄,用来取消不需要执行的内容
var handle =null;

var hover = 'hover';
var index = 0;


//显示下拉菜单
var doDropMenu = function(arrow){
	//如果被用户用ESC清空数据
	if (target==null) {return;}
	if (!arrow) {
		index = 0;
		for(var i=0;i<count;i++){
			if ($(target[i]).hasClass(hover)) {
				$(target[i]).removeClass(hover);
			}
		}
	}
	//如果获取候选词数量是0也不应该显示
	if(keyWord == ""|| count==0){
		warpBox.hide();
	}else{
		warpBox.show();
	}
}

//进行搜索
//添加第二个参数,为默认有转向地址的候选词提供服务
var doSearch = function(search, justRedirect){
	//如果有候选词,那么直接使用候选词地址
	if (justRedirect) {
		if(newWindows){
			window.open(justRedirect);
		}else{
			document.location.href = justRedirect;
		}
	//没有只好使用搜索的了
	}else{
		if(newWindows){
			window.open(searchHost+search+sarchPrefix);
		}else{
			document.location.href = searchHost+search+sarchPrefix;
		}		
	}
}

//清除之前的事件绑定以及内容
var doClean = function(){
	if(target !== null){
		//解除之前的数据绑定
		warp.unbind('mouseover');
		target.unbind('click');
		target = null;
	}
	//无论如何都应该清空和隐藏下拉的容器
	warp.empty();
	warpBox.hide();
}
//用JSON填充当前的数据
//因为需要回调,添加到JQUERY的全局变量中
$.extend({
	doJSON : function(resp){
		//首先扫地
		var sData = document.getElementById('syData');
		if(sData){sData.parentNode.removeChild(sData)}
	
		if (resp) {
			window.$SAE['QUERY_WORD'] = resp;
			return;
		}else{
			var sData = document.createElement('script');
			sData.id = 'syData';
			sData.type = 'text/javascript';
			sData.src = dataHost + 'keyword=' + autoWordText.val() +'&callback=jQuery.doJSON';
			document.getElementsByTagName('body')[0].appendChild(sData);
		}
	}
});


//获取关键词
var doQuery = function(){
	//之前的绑定单独抽象
	//因为要进行动态创建元素重新绑定
	var onLine = function(e){
		var curTR = $(e.target).parents('tr')
			target.removeClass(hover);
			curTR.addClass(hover);
		for(var i=0;i<count;i++){
			if ($(target[i]).hasClass(hover)) {
				index = i;
			}
		}
		e.stopPropagation();
	}

	var clickLine = function(e){
		var curTR = $(e.target).parents('tr');
			curTR.removeClass(hover);
		//写习惯DOM了- -!之前使用JQ=>DOM
		//然后操作,其实既然选择JQ
		//那么就通用操作都用JQ的封装方法吧
		keyWord = curTR.text();

		if (curTR.attr('data-url')) {
			//如果包含直接的定义跳转,就不去进行搜索
			//使用新的搜索函数进行搜索
			doSearch('',curTR.attr('data-url'));
		}else{
			//调用搜索
			doSearch(keyWord);
		}
		event.stopPropagation();
	}

	//高亮要搜索的内容
	//使用B标签是因为节约流量(@百度)
	var wordHighlight = function(word, keyARR){
		//然后把其他的字符都高亮
		for(var xx in keyARR){
			//排除被分割的字符是空
			if (keyARR[xx] !== '') {
				word = word.replace(keyARR[xx], '<b>'+keyARR[xx]+'</b>');
			}
		}
		return word;
	}

	//将原来的过程封装入函数
	//利于延时执行
	var coreQuery = function(){	
		//获取内容之前打扫卫生
		doClean();
		//因为是延时执行,所以用户碰巧在一瞬间把内容清空
		//延时的任务还是进行了,所以要判断内容是否为空
		if(autoWordText.val() == ''){return;}
		//重新获得并整理数据
		var data = $SAE.QUERY_WORD;
		var query = data.query;
		var list = data.result;
		if (list.length == 0) {return;}

		//最后要输出的HTML变量/
		//和处理过程中用的临时数组
		var tmpSTR = '';
		var tmpLine = '';
		var strHTML = [];
		for(var oo in list){
			//把内容分割为数组根据内容
			if(list[oo] instanceof Array){
				tmpSTR = list[oo][0];
			}else{
				tmpSTR = list[oo];
			}

			//用输入的内容分割字符串,排除要高亮的部分
			//先把内容替换为<tr><td>***关键词***</tr></td>

			//对内容进行高亮处理
			tmpLine = tmpSTR.replace(query, '<span>'+query+'</span>');
			tmpLine = wordHighlight(tmpLine, tmpSTR.split(query));
			//对内容进行链接处理
			if(list[oo] instanceof Array){
				tmpLine ='<tr data-url="'+list[oo][1]+'"><td>'+tmpLine+'</td></tr>';
			}else{
				tmpLine ='<tr><td>'+tmpLine+'</td></tr>';
			}

			//把整理好的内容添加到要输出的数组中
			strHTML.push(tmpLine);
		}
		//把整理好的数组合并字符串添加到文档
		autoWordList.html(strHTML.join(''));
		//展示自动完成的列表
		warpBox.show();
		//刷新数据
		//重新获取数据并绑定事件
		warp = autoWordList.find('tbody');
		target = autoWordList.find('tr');
		count = target.length;
		warp.bind('mouseover', onLine);
		target.bind('click',clickLine);
	}


	//添加延时执行,给用户一个选择的机会
	//如果用户在搜索新内容前移动光标则取消请求
	//而且可以降低服务器被请求数量
	//清除上一次延时任务
	clearTimeout(handle);
	//进入延时执行...
	//从服务器取数据
	$.doJSON();
	handle = setTimeout(coreQuery,300);
	//延时执行完毕...
}

//限制输入最大长度
var limitTextCount = function(){
	var safeSTR = autoWordText.val();
	//如果内容是空,不进行处理
	if (safeSTR=='') {return;}
	if(safeSTR.length>keyWordMaxLen){
		autoWordText.val(safeSTR.substring(0,keyWordMaxLen));
	}
}

autoWordText.bind('keydown', function(e){
	//如果按下ESC清空了缓存
	if(target == null){return;}

	var userSelect = function(){
		for(var i=0;i<count;i++){
			if ($(target[i]).hasClass(hover)){
				$(target[i]).removeClass(hover);
			}
		}
		$(target[index-1]).addClass(hover);
		autoWordText.val($(target[index-1]).text());
		keyWord = autoWordText.val();
	}

	//因为每次按下字符都调用这个函数
	//所以除了使用DOM外真的想不到什么更节约的方法
	//这里为了扩展性取元素未使用
	var setPointEnd = function(){
		if (textCursor.setSelectionRange){
			var len =  textCursor.value.length;
			setTimeout(function(){
			textCursor.setSelectionRange(len, len);
			textCursor.focus();
		}, 0);
		}else if (textCursor.createTextRange){
			var txtRange=textCursor.createTextRange();
			txtRange.moveStart("character",txtRange.text.length);
			txtRange.select();
		}
	}

	switch(e.keyCode){
		case 38:
			//如果用户使用上下方向键
			//说明在选择内容,那么取消要进行的刷新数据
			clearTimeout(handle);
			if (index <= 1) {
				index = count;
			} else {
				index--;
			}
			userSelect();
			setPointEnd();
			doDropMenu(true);
			break;
		case 40:
			//如果用户使用上下方向键
			//说明在选择内容,那么取消要进行的刷新数据
			clearTimeout(handle);
			if (index > count-1) {
				index = 1;
			} else {
				index++;
			}
			userSelect();
			doDropMenu(true);
			break;
		case 13:
			if (index==0) {
				keyWord = autoWordText.val();
			}else{
				keyWord = $(target[index-1]).text();
			}
			//内容不为空的时候
			if (keyWord !=''){
				//调用搜索
				doSearch(keyWord);
			}
			break
		case 27:
			index=0;
			//按下ESC后,应该清空上一次的搜索结果
			//而且既然ESC,那么就是重新输入需求
			//取消要进行的刷新数据
			clearTimeout(handle);
			//并且打扫卫生
			doClean();
			break;
		default:
		//限制输入的长度
			limitTextCount();
	}

	e.stopPropagation();
});

//内容改变不仅仅是按键输入,还有CTRL+V
autoWordText.bind('paste', function(){
	limitTextCount();
});


if($.browser.msie){
	autoWordText.on('propertychange', function(){
		//刷新备选列表
		doQuery();
		keyWord = autoWordText.val();
		//如果关键词为空,那么不展示下拉菜单
		if (keyWord!=='') {
			doDropMenu();
		}
	});
}else{
	autoWordText.on('input', function(){
		//刷新备选列表
		doQuery();
		keyWord = autoWordText.val();
		//如果关键词为空,那么不展示下拉菜单
		if (keyWord!=='') {
			doDropMenu();
		}
	});
}

感觉是不是距离成品越来越近了,那么把没有做的功能加上,关键是可以从数据源取数据。

优化和抽象: http://thecdn.sinaapp.com/page/demo/jq-auto-complete/step5.html

var searchBtn = $('#search');
searchBtn.bind('mousedown',function(){
	$(this).addClass('clicked');
	//开始搜索
	//搜索代码...
});
searchBtn.bind('mouseout',function(){
	$(this).removeClass('clicked');
});


window.$SAE = [];
//给一份默认的热词列表,优化体验
window.$SAE['QUERY_WORD'] = 
{
	query:"新浪",
	result:[["新浪微博!","http://weibo.com/"],
			"新浪微博登陆!",
			"新浪邮箱!",
			"新浪nba",
			"新浪爱问",
			"新浪体育",
			"新浪博客",
			"新浪爱问共享资料",
			"新浪网",
			"新浪邮箱登陆"]
};

//自动完成列表
var autoWordList = $('table#wordlst');
//输入框
var autoWordText = $('#autocomplete');
//为输入框光标缓存一份
var textCursor = document.getElementById('autocomplete'); 

var warp = autoWordList.find('tbody');
var warpBox = autoWordList.parent();

var target = autoWordList.find('tr');
var count = target.length;

//搜索设置
var searchHost = 'http://www.baidu.com/s?wd=';
//JSON数据接口
var dataHost = 'http://localhost/query.php?';
var keyWord = '';
var sarchPrefix = '+site:sae.sina.com.cn';
var newWindows = true;
//输入框限制搜索内容长度
var keyWordMaxLen = 100 -1;

//延时执行的句柄,用来取消不需要执行的内容
var handle =null;

var hover = 'hover';
var index = 0;


//显示下拉菜单
var doDropMenu = function(arrow){
	//如果被用户用ESC清空数据
	if (target==null) {return;}
	if (!arrow) {
		index = 0;
		for(var i=0;i<count;i++){
			if ($(target[i]).hasClass(hover)) {
				$(target[i]).removeClass(hover);
			}
		}
	}
	//如果获取候选词数量是0也不应该显示
	if(keyWord == ""|| count==0){
		warpBox.hide();
	}else{
		warpBox.show();
	}
}

//进行搜索
//添加第二个参数,为默认有转向地址的候选词提供服务
var doSearch = function(search, justRedirect){
	//如果有候选词,那么直接使用候选词地址
	if (justRedirect) {
		if(newWindows){
			window.open(justRedirect);
		}else{
			document.location.href = justRedirect;
		}
	//没有只好使用搜索的了
	}else{
		if(newWindows){
			window.open(searchHost+search+sarchPrefix);
		}else{
			document.location.href = searchHost+search+sarchPrefix;
		}		
	}
}

//清除之前的事件绑定以及内容
var doClean = function(){
	if(target !== null){
		//解除之前的数据绑定
		warp.unbind('mouseover');
		target.unbind('click');
		target = null;
	}
	//无论如何都应该清空和隐藏下拉的容器
	warp.empty();
	warpBox.hide();
}
//用JSON填充当前的数据
//因为需要回调,添加到JQUERY的全局变量中
$.extend({
	doJSON : function(resp){
		//首先扫地
		var sData = document.getElementById('syData');
		if(sData){sData.parentNode.removeChild(sData)}
	
		if (resp) {
			window.$SAE['QUERY_WORD'] = resp;
			return;
		}else{
			var sData = document.createElement('script');
			sData.id = 'syData';
			sData.type = 'text/javascript';
			sData.src = dataHost + 'keyword=' + autoWordText.val() +'&callback=jQuery.doJSON';
			document.getElementsByTagName('body')[0].appendChild(sData);
		}
	}
});


//获取关键词
var doQuery = function(){
	//之前的绑定单独抽象
	//因为要进行动态创建元素重新绑定
	var onLine = function(e){
		var curTR = $(e.target).parents('tr')
			target.removeClass(hover);
			curTR.addClass(hover);
		for(var i=0;i<count;i++){
			if ($(target[i]).hasClass(hover)) {
				index = i;
			}
		}
		e.stopPropagation();
	}

	var clickLine = function(e){
		var curTR = $(e.target).parents('tr');
			curTR.removeClass(hover);
		//写习惯DOM了- -!之前使用JQ=>DOM
		//然后操作,其实既然选择JQ
		//那么就通用操作都用JQ的封装方法吧
		keyWord = curTR.text();

		if (curTR.attr('data-url')) {
			//如果包含直接的定义跳转,就不去进行搜索
			//使用新的搜索函数进行搜索
			doSearch('',curTR.attr('data-url'));
		}else{
			//调用搜索
			doSearch(keyWord);
		}
		event.stopPropagation();
	}

	//高亮要搜索的内容
	//使用B标签是因为节约流量(@百度)
	var wordHighlight = function(word, keyARR){
		//然后把其他的字符都高亮
		for(var xx in keyARR){
			//排除被分割的字符是空
			if (keyARR[xx] !== '') {
				word = word.replace(keyARR[xx], '<b>'+keyARR[xx]+'</b>');
			}
		}
		return word;
	}

	//将原来的过程封装入函数
	//利于延时执行
	var coreQuery = function(){	
		//获取内容之前打扫卫生
		doClean();
		//因为是延时执行,所以用户碰巧在一瞬间把内容清空
		//延时的任务还是进行了,所以要判断内容是否为空
		if(autoWordText.val() == ''){return;}
		//重新获得并整理数据
		var data = $SAE.QUERY_WORD;
		var query = data.query;
		var list = data.result;
		if (list.length == 0) {return;}

		//最后要输出的HTML变量/
		//和处理过程中用的临时数组
		var tmpSTR = '';
		var tmpLine = '';
		var strHTML = [];
		for(var oo in list){
			//把内容分割为数组根据内容
			if(list[oo] instanceof Array){
				tmpSTR = list[oo][0];
			}else{
				tmpSTR = list[oo];
			}

			//用输入的内容分割字符串,排除要高亮的部分
			//先把内容替换为<tr><td>***关键词***</tr></td>

			//对内容进行高亮处理
			tmpLine = tmpSTR.replace(query, '<span>'+query+'</span>');
			tmpLine = wordHighlight(tmpLine, tmpSTR.split(query));
			//对内容进行链接处理
			if(list[oo] instanceof Array){
				tmpLine ='<tr data-url="'+list[oo][1]+'"><td>'+tmpLine+'</td></tr>';
			}else{
				tmpLine ='<tr><td>'+tmpLine+'</td></tr>';
			}

			//把整理好的内容添加到要输出的数组中
			strHTML.push(tmpLine);
		}
		//把整理好的数组合并字符串添加到文档
		autoWordList.html(strHTML.join(''));
		//展示自动完成的列表
		warpBox.show();
		//刷新数据
		//重新获取数据并绑定事件
		warp = autoWordList.find('tbody');
		target = autoWordList.find('tr');
		count = target.length;
		warp.bind('mouseover', onLine);
		target.bind('click',clickLine);
	}


	//添加延时执行,给用户一个选择的机会
	//如果用户在搜索新内容前移动光标则取消请求
	//而且可以降低服务器被请求数量
	//清除上一次延时任务
	clearTimeout(handle);
	//进入延时执行...
	//从服务器取数据
	$.doJSON();
	handle = setTimeout(coreQuery,300);
	//延时执行完毕...
}

//限制输入最大长度
var limitTextCount = function(){
	var safeSTR = autoWordText.val();
	//如果内容是空,不进行处理
	if (safeSTR=='') {return;}
	if(safeSTR.length>keyWordMaxLen){
		autoWordText.val(safeSTR.substring(0,keyWordMaxLen));
	}
}

autoWordText.bind('keydown', function(e){
	//如果按下ESC清空了缓存
	if(target == null){return;}

	var userSelect = function(){
		for(var i=0;i<count;i++){
			if ($(target[i]).hasClass(hover)){
				$(target[i]).removeClass(hover);
			}
		}
		$(target[index-1]).addClass(hover);
		autoWordText.val($(target[index-1]).text());
		keyWord = autoWordText.val();
	}

	//因为每次按下字符都调用这个函数
	//所以除了使用DOM外真的想不到什么更节约的方法
	//这里为了扩展性取元素未使用
	var setPointEnd = function(){
		if (textCursor.setSelectionRange){
			var len =  textCursor.value.length;
			setTimeout(function(){
			textCursor.setSelectionRange(len, len);
			textCursor.focus();
		}, 0);
		}else if (textCursor.createTextRange){
			var txtRange=textCursor.createTextRange();
			txtRange.moveStart("character",txtRange.text.length);
			txtRange.select();
		}
	}

	switch(e.keyCode){
		case 38:
			//如果用户使用上下方向键
			//说明在选择内容,那么取消要进行的刷新数据
			clearTimeout(handle);
			if (index <= 1) {
				index = count;
			} else {
				index--;
			}
			userSelect();
			setPointEnd();
			doDropMenu(true);
			break;
		case 40:
			//如果用户使用上下方向键
			//说明在选择内容,那么取消要进行的刷新数据
			clearTimeout(handle);
			if (index > count-1) {
				index = 1;
			} else {
				index++;
			}
			userSelect();
			doDropMenu(true);
			break;
		case 13:
			if (index==0) {
				keyWord = autoWordText.val();
			}else{
				keyWord = $(target[index-1]).text();
			}
			//内容不为空的时候
			if (keyWord !=''){
				//调用搜索
				doSearch(keyWord);
			}
			break
		case 27:
			index=0;
			//按下ESC后,应该清空上一次的搜索结果
			//而且既然ESC,那么就是重新输入需求
			//取消要进行的刷新数据
			clearTimeout(handle);
			//并且打扫卫生
			doClean();
			break;
		default:
		//限制输入的长度
			limitTextCount();
	}

	e.stopPropagation();
});

//内容改变不仅仅是按键输入,还有CTRL+V
autoWordText.bind('paste', function(){
	limitTextCount();
});


if($.browser.msie){
	autoWordText.on('propertychange', function(){
		//刷新备选列表
		doQuery();
		keyWord = autoWordText.val();
		//如果关键词为空,那么不展示下拉菜单
		if (keyWord!=='') {
			doDropMenu();
		}
	});
}else{
	autoWordText.on('input', function(){
		//刷新备选列表
		doQuery();
		keyWord = autoWordText.val();
		//如果关键词为空,那么不展示下拉菜单
		if (keyWord!=='') {
			doDropMenu();
		}
	});
}

这里随便写了一个PHP,模拟输出搜索内容的返回数据

<?php
/* SOULTEARY.COM
   _____ ____  __  ____  _______________    ______  __
  / ___// __ \/ / / / / /_  __/ ____/   |  / __ \ \/ /
  \__ \/ / / / / / / /   / / / __/ / /| | / /_/ /\  / 
 ___/ / /_/ / /_/ / /___/ / / /___/ ___ |/ _, _/ / /  
/____/\____/\____/_____/_/ /_____/_/  |_/_/ |_| /_/   
*/

/***
*	简单的搜索示例文件
					***/
//首先你要设置HEADER,如果你不想浏览器提示异常的话
header('Content-Type:application/javascript');

//初始化关键词
$KeyWord = '';
if (isset($_REQUEST['keyword']) && !empty($_REQUEST['keyword'])) {
	//这里需要加入过滤判断
	$KeyWord = $_REQUEST['keyword'];
}else{
	//空的话,不继续执行
	die();
}

//初始化回调函数
$CallBack = '';
if (isset($_REQUEST['callback']) && !empty($_REQUEST['callback'])) {
	$CallBack = $_REQUEST['callback'];
}

//这里是例子,
//真实环境是从数据库或者MC缓存中取
//下面就简单的用测试关键字来搞吧
switch ($KeyWord) {
	case '新':
			$Data = '{
				query:"'.$KeyWord.'",
				result:[["新浪微博!","http://weibo.com/"],
						"新浪微博登陆!",
						"新浪邮箱!",
						"新浪nba",
						"新浪爱问",
						"新浪体育",
						"新浪博客",
						"新浪爱问共享资料",
						"新浪网",
						"新浪邮箱登陆"]
			}';
		break;
	case '新浪':
			$Data = '{
				query:"'.$KeyWord.'",
				result:[["新浪微博!","http://weibo.com/"],
						"新浪微博登陆!",
						"新浪邮箱!",
						"新浪nba",
						"新浪爱问",
						"新浪体育",
						"新浪博客",
						"新浪爱问共享资料",
						"新浪网",
						"新浪邮箱登陆"]
			}';
		break;
	case '苏':
			$Data = '{
				query:"'.$KeyWord.'",
				result:[["苏洋博客","http://soulteary.com/"],
						"苏洋微博",
						"苏洋邮箱",
						"苏洋nba",
						"苏洋爱问",
						"苏洋体育",
						"苏洋博客",
						"苏洋爱问共享资料",
						"苏洋网",
						"苏洋邮箱登陆"]
			}';
		break;
	case '苏洋':
			$Data = '{
				query:"'.$KeyWord.'",
				result:[["苏洋博客","http://soulteary.com/"],
						"苏洋微博",
						"苏洋邮箱",
						"苏洋nba",
						"苏洋爱问",
						"苏洋体育",
						"苏洋博客",
						"苏洋爱问共享资料",
						"苏洋网",
						"苏洋邮箱登陆"]
			}';
		break;
	default:
			$Data = '{
				query:"'.$KeyWord.'",
				result:[]
			}';
		break;
}


//最后就是输出
echo $CallBack.'('.$Data.')';
?>

最后就是为了以后的复用,插件化

插件化: http://thecdn.sinaapp.com/page/demo/jq-auto-complete/index.html

调用方法还是简单的


	$('#warp').autoComplete({
		wdList:'table#wordlst',					//下拉列表
		wdText:'#autocomplete',					//输入框
		engine:'http://www.baidu.com/s?wd=',	//搜索引擎地址
		kwFix:'+site:sae.sina.com.cn',			//搜索引擎参数
		djson:'http://localhost/query.php?',	//数据源地址
		wdMax:100,								//最多输入字符
		newWin:false,							//在新窗口打开
		wait:300,								//延时操作(毫秒)
		gData:$SAE['QUERY_WORD'],				//全局变量
		hover:'hover'							//鼠标和方向键给于高亮的类名

写在最后,又写完了一篇。

因为很多内容在之前的两篇,还有之前的注释中提到过,所以不会再次赘述,如果有疑问,不妨先看看之前的内容。

实在无解,可以留言提问,一起学习,一起成长。我觉得内容还是很简单的,尤其是分解动作之后。