本程序仿照Google搜索自动完成功能,当用户在搜索文本框中输入搜索关键字时,利用Ajax请求服务器数据库获取相匹配数据返回,然后以列表方式显示在搜索文本框的下方。
界面截图如下:
一、对搜索文本框的简单封装:
1 using System;
2 using System.Text;
3 using System.Web.UI.WebControls;
4 using System.ComponentModel;
5 using System.IO;
6 using System.Collections;
7
8 namespace MyControls
9 {
10 public class AutoCompleteTextBox : TextBox
11 {
12 /// <summary>
13 /// 设置或获取资源文件夹目录
14 /// </summary>
15 [Bindable(true)]
16 [Category("搜索")]
17 [Description("设置或获取资源文件夹目录")]
18 public String ClientScriptPath
19 {
20 get
21 {
22 String s = (String)ViewState["ClientScriptPath"];
23 return ((s == null) ? String.Empty : s);
24 }
25
26 set
27 {
28 ViewState["ClientScriptPath"] = value;
29 }
30 }
31
32 protected override void OnPreRender(EventArgs e)
33 {
34 string strJsPath = base.ResolveUrl(Path.Combine(this.ClientScriptPath, @"SearchControlIntelligent.js"));
35 if (!Page.ClientScript.IsClientScriptBlockRegistered(this.GetType(), "SearchControlIntelligentScript"))
36 {
37 Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "SearchControlIntelligentScript", "<script type=\"text/javascript\" src=\"" + strJsPath + "\"></script>", false);
38 }
39
40 StringBuilder strInitScript = new StringBuilder(50);
41 strInitScript.Append("<script type=\"text/javascript\">");
42 strInitScript.AppendFormat("initQueryCode('{0}')", this.ClientID);
43 strInitScript.Append("</script>");
44
45 if (!Page.ClientScript.IsStartupScriptRegistered(this.GetType(), "InitScript" + this.UniqueID))
46 {
47 Page.ClientScript.RegisterStartupScript(this.GetType(), "InitScript" + this.UniqueID, strInitScript.ToString());
48 }
49 base.OnPreRender(e);
50 }
51
52 protected override void OnLoad(EventArgs e)
53 {
54 AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProSearchService));
55 base.OnLoad(e);
56 }
57 }
58 }
二、通过搜索字符获取数据库中相匹配条目
1 using System;
2 using System.Data.SqlClient;
3 using System.Data;
4 using System.Configuration;
5
6 namespace MyControls
7 {
8 [AjaxPro.AjaxNamespace("AjaxProSearchService")]
9 public class AjaxProSearchService
10 {
11 public static string CONN_STRING = ConfigurationManager.AppSettings["SqlServerConnectionString"];
12
13 [AjaxPro.AjaxMethod]
14 public static DataSet GetKeywordByChar(string key)
15 {
16 if (key == null || key.Length == 0) return null;
17 using (SqlConnection con = new SqlConnection(CONN_STRING))
18 {
19 SqlCommand comd = new SqlCommand(string.Format("select Keyword from AutoCompleteSearch where Keyword like '%{0}%'", key), con);
20 SqlDataAdapter sqlDa = new SqlDataAdapter(comd);
21 con.Open();
22 DataSet ds = new DataSet();
23 sqlDa.Fill(ds);
24 return ds;
25 }
26 }
27 }
28 }
三、将匹配的条目以列表方式显示在搜索文本框下方,添加键盘和鼠标的响应操作
1 var DIV_BG_COLOR = "#FFFFFF"; //智能提示框背景颜色
2 var DIV_HIGHLIGHT_COLOR = "#B4CCEB" //智能提示框中被选中条目背景颜色
3 var DIV_FONT = "Arial"; //智能提示框中文字颜色
4 var DIV_PADDING = "2px"; //智能提示框中条目padding
5 var DIV_BORDER = "1px solid #020086"; //智能提示框边框颜色
6 var queryField; //搜索文本框
7 var divName; //始智能提示div的id
8 var lastVal = ""; //上次搜索的关键字
9 var globalDiv; //智能提示框div
10 var divFormatted = false; //标示智能提示框div是否已被设置样式
11
12 /*
13 给搜索文本框注册事件
14 queryFiledId:搜索框id
15 */
16 function initQueryCode(queryFiledId) {
17 queryField = document.getElementById(queryFiledId);
18 queryField.onblur = hideDiv;
19 queryField.onkeydown = keypressHandler;
21 divName = "querydiv"; //初始智能提示div的id
22 setTimeout("mainLoop()", 100);
23 }
24
25 /*
26 获取智能提示框div
27 divID:智能提示框div的id
28 */
29 function getDiv(divID) {
30 if (!globalDiv) {
31 if (!document.getElementById(divID)) {
32 var newNode = document.createElement("div");
33 newNode.setAttribute("id", divID);
34 document.body.appendChild(newNode);
35 }
36 globalDiv = document.getElementById(divID); //将智能提示框div的引用保存到globalDiv变量中
37 var x = queryField.offsetLeft;
38 var y = queryField.offsetTop + queryField.offsetHeight;
39 var parent = queryField;
40 while (parent.offsetParent) {
41 parent = parent.offsetParent;
42 x += parent.offsetLeft;
43 y += parent.offsetTop;
44 }
45 if (!divFormatted) {
46 globalDiv.style.backgroundColor = DIV_BG_COLOR;
47 globalDiv.style.fontFamily = DIV_FONT;
48 globalDiv.style.padding = DIV_PADDING;
49 globalDiv.style.border = DIV_BORDER;
50 globalDiv.style.width = queryField.style.width;
51 globalDiv.style.fontSize = "90%";
52 globalDiv.style.position = "absolute";
53 globalDiv.style.left = x + "px";
54 globalDiv.style.top = y + "px";
55 globalDiv.style.visibility = "hidden";
56 globalDiv.style.zIndex = 10000;
57 divFormatted = true;
58 }
59 }
60 return globalDiv;
61 }
62
63 /*
64 显示智能提示div
65 resultArray:提示结果集
66 */
67 function showQueryDiv(resultArray) {
68 var div = getDiv(divName); //获取智能提示div,并移除其中条目
69 while (div.childNodes.length > 0) {
70 div.removeChild(div.childNodes[0]);
71 }
72 //每一个提示条目放在div中
73 for (var i = 0; i < resultArray.length; i++) {
74 var itemDiv = document.createElement("div");
75 itemDiv.style.cursor = "pointer";
76 itemDiv.style.padding = "2px 0px 2px 0px";
77 itemDiv.style.width = div.style.width;
78 _unhighlightResult(itemDiv); //给每一个条目赋背景色
79 //鼠标按下,将当前条目文字赋给搜索框,关闭智能提示div
80 itemDiv.onmousedown = selectResult;
81 //鼠标经过条目设置其高亮
82 itemDiv.onmouseover = highlightResult;
83 //鼠标离开,取消高亮
84 itemDiv.onmouseout = unhighlightResult;
85 //每一提示条目的文字放在span中
86 var value = document.createElement("span");
87 value.className = "value";
88 value.style.textAlign = "left";
89 value.style.fontWeight = "bold";
90 value.innerHTML = resultArray[i];
91 itemDiv.appendChild(value);
92 div.appendChild(itemDiv);
93 }
94 showDiv(resultArray.length > 0);
95 }
96
97 function selectResult() {
98 _selectResult(this);
99 }
100
101 /*
102 选中条目,关闭智能提示框
103 item:被选中的条目
104 */
105 function _selectResult(item) {
106 var spans = item.getElementsByTagName("span");
107 if (spans) {
108 for (var i = 0; i < spans.length; i++) {
109 if (spans[i].className == "value") {
110 queryField.value = spans[i].innerHTML;
111 lastVal = val = escape(queryField.value);
112 mainLoop();
113 queryField.focus();
114 showDiv(false);
115 return;
116 }
117 }
118 }
119 }
120
121 function highlightResult() {
122 _highlightResult(this);
123 }
124
125 /*
126 设置条目高亮(设置背景色突出显示)
127 item:设置高亮的条目
128 */
129 function _highlightResult(item) {
130 item.style.backgroundColor = DIV_HIGHLIGHT_COLOR;
131 }
132
133 function unhighlightResult() {
134 _unhighlightResult(this);
135 }
136
137 /*
138 取消高亮(还原背景色)
139 item:取消高亮的条目
140 */
141 function _unhighlightResult(item) {
142 item.style.backgroundColor = DIV_BG_COLOR;
143 }
144
145 /*
146 显示智能提示框
147 isShow:是否显示bool值
148 */
149 function showDiv(isShow) {
150 var div = getDiv(divName);
151 if (isShow) {
152 div.style.visibility = "visible";
153 } else {
154 div.style.visibility = "hidden";
155 }
156 }
157
158 function hideDiv() {
159 showDiv(false);
160 }
161
162 /*
163 鼠标按下事件处理
164 e:事件对象
165 */
166 function keypressHandler(e) {
167 var div = getDiv(divName);
168 if (div.style.visibility == "hidden") {
169 return true;
170 }
171 var key = -1;
172 if (window.event) {
173 key = window.event.keyCode; //IE下获取键值
174 } else {
175 key = e.which; ////FireFox,其它
176 }
177 var KEYUP = 38; //上方向键
178 var KEYDOWN = 40; //下方向键
179 var KEYENTER = 13; //回车键
180 var KEYTAB = 9; //tab键
181 if (key != KEYUP && key != KEYDOWN && key != KEYENTER && key != KEYTAB) {
182 return true;
183 }
184 var selNum = getSelectedItemNum(div); //获取当前处于选中状态的条目序号
185 var selItem = setSelectedItem(div, selNum);
186 if (key == KEYENTER || key == KEYTAB) {
187 if (selItem) {
188 _selectResult(selItem);
189 }
190 e.cancelBubble = true;
191 return false;
192 } else {
193 if (key == KEYUP) {
194 selItem = setSelectedItem(div, selNum - 1);
195 }
196 if (key == KEYDOWN) {
197 selItem = setSelectedItem(div, selNum + 1);
198 }
199 if (selItem) {
200 _highlightResult(selItem);
201 }
202 }
203 showDiv(true);
204 return true;
205 }
206
207 /*
208 获取选中条目的序号
209 div:智能提示框div
210 */
211 function getSelectedItemNum(div) {
212 var items = div.getElementsByTagName("div");
213 if (items) {
214 for (var i = 0; i < items.length; i++) {
215 //没有选中条目都被设置与提示框颜色相同
216 if (items[i].style.backgroundColor != div.style.backgroundColor) {
217 return i;
218 }
219 }
220 }
221 return -1;
222 }
223
224 /*
225 设置条目为选中状态
226 div:智能提示框div,itemNum:条目序号
227 */
228 function setSelectedItem(div, itemNum) {
229 var thisDiv;
230 var divs = div.getElementsByTagName("div");
231 if (divs) {
232 for (var i = 0; i < divs.length; i++) {
233 if (i == itemNum) {
234 _highlightResult(divs[i]);
235 thisDiv = divs[i];
236 }
237 else {
238 _unhighlightResult(divs[i]);
239 }
240 }
241 }
242 return thisDiv;
243 }
244
245 var mainLoop = function() {
246 var val = escape(queryField.value);
247 if (lastVal != val) {
248 var response = AjaxProSearchService.GetKeywordByChar(val);
249 var datatable = response.value.Tables[0];
250 var arrayKey = new Array();
251 if (datatable.Rows.length > 0) {
252 for (var i = 0; i < datatable.Rows.length; i++) {
253 arrayKey.push(datatable.Rows[i].Keyword);
254 }
255 }
256 showQueryDiv(arrayKey);
257 lastVal = val;
258 }
259 setTimeout("mainLoop()", 100);
260 return true;
261 }
需要说明一下的是,程序中用到了AjaxPro这个Ajax框架,AjaxPro支持通过javascript访问服务端方法。其使用方法很简单。
1、添加对AjaxPro.2.dll的引用。
2、在Web.config中注册该组件的处理程序,代码如下:
<httpHandlers>
<!--AjaxPro.2.dll-->
<add verb="POST,GET" path="ajaxpro/*.ashx" type="AjaxPro.AjaxHandlerFactory,AjaxPro.2"/>
……
</httpHandlers>
3、在使用AjaxPro组件的页面Page_Load事件中注册类类型:
AjaxPro.Utility.RegisterTypeForAjax(typeof(AjaxProSearchService));//AjaxProSearchService为使用AjaxPro组件的页面类名
4、使用实例:
服务器端方法的申明:
[AjaxPro.AjaxMethod] //必不可少
public static DataSet GetKeywordByChar(string key)
{
……
}
客服javascript端调用:
//AjaxProSearchService:访问的服务器端方法的类名,val:与服务器端方法参数保持一致
var response = AjaxProSearchService.GetKeywordByChar(val);
很简单的一个功能,还有很多细节以后慢慢完善。