基于AJAX的自动完成

基于AJAX的自动完成

     我想大家在访问某些网站的时候都曾见到过基于Ajax的自动完成功能,比如    


一、引出Ajax的自动完成
     现在要实现一个员工信息查询的功能,即根据输入的名字检索员工的详细信息。这是一个简单的数据表查询,在
ASP.NET中实现这样的功能是比较简单的.
                 
      从上面可以看出,这种员工信息查询功能还存在一些不足,比如用户可能记不全员工的名字,只记得前面几个字母是什么,这样用户只能根据记忆猜测,一遍遍地尝试。如果在用户输入的同时,输入框下方可以给出相应的提示,辅助用户输入,那么用户进行检索的速度和成功率就会大大提高.这就是基于Ajax的自动完成功能.
               

二、自动完成功能的实现
     实现这样的功能需要按以下的步骤进行。
      · 服务器端提供GetSearchItems方法给客户端,用来返回满足条件的员工列表。
      · 客户端的输入框需要增加onkeydown响应函数,以便即时获取满足条件的员工列表。
      · 通过客户端的JavaScript动态列出待选结果的列表,同时还要提供键盘和鼠标的响应。

三、服务器端实现
     本文采用AjaxPro.NET作为Ajax开发框架,首先为使用AjaxPro.NET做一些准备工作。 添加对AjaxPro.dll的引用,修改Web.config配置文件,在system.web节点下加入如下配置:

1 < httpHandlers >
2 <!--  Register the ajax handler  -->
3 < add  verb ="POST,GET"  path ="ajaxpro/*.ashx"  type ="AjaxPro.AjaxHandlerFactory, AjaxPro"   />
4 </ httpHandlers >

    在页面后台代码( Default.aspx.cs )的 Page_Load 方法中增加下面的代码
1 protected   void  Page_Load( object  sender, EventArgs e)
2 {
3        AjaxPro.Utility.RegisterTypeForAjax(typeof(_Default));
4}

    下面定义提供给客户端调用的方法GetSearchItems(),参数query为模糊查询的关键字值:
 1 [AjaxPro.AjaxMethod()]
 2 public  ArrayList GetSearchItems( string  query)
 3 {
 4    ArrayList items = new ArrayList();
 5    StringBuilder queryString = new StringBuilder();
 6    queryString.Append("select employeeid,lastname,firstname,title,titleofcourtesy from dbo.Employees");
 7    queryString.Append(" where firstname like '%" + query + "%'");
 8
 9    DataSet ds = DataBase.Instance.ReturnDataSet(queryString.ToString());
10    for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
11    {
12       items.Add(ds.Tables[0].Rows[i][2].ToString());
13    }

14    return items;
15}
GetSearchItems 方法返回一个 ArrayList 对象,它将包含所有以用户输入字符串的员工名字。

四、客户端实现
    相对于服务器端的方法而言,客户端的处理要复杂得多。首先来分析如何根据服务器端返回的 ArrayList 对象展示结果。 这里用到了 Web 编程中“层”( div )的概念,通过 JavaScript DOM 创建一个新的层 div ,将 ArrayList 中的每一个条目都作为其子节点加入到 div 中,而每一个条目也被看作是一个 div ,其中具体的文本内容则是一个 span 对象。

    除了显示待选的结果之外,下拉区域还要对键盘、鼠标事件做出响应;为了实时地显示待选结果,还需要定时更新待选结果的列表。这些功能都封装在 lookup.js中。下面是lookeu.js的定义:
  1 //  下拉区背景色
  2 var  DIV_BG_COLOR  =   " #EEE " ;
  3 //  高亮显示条目颜色
  4 var  DIV_HIGHLIGHT_COLOR  =   " #C30 " ;
  5 //  字体
  6 var  DIV_FONT  =   " Arial " ;
  7 //  下拉区内补丁大小
  8 var  DIV_PADDING  =   " 2px " ;
  9 //  下拉区边框样式
 10 var  DIV_BORDER  =   " 1px solid #CCC " ;
 11
 12
 13 //  文本输入框
 14 var  queryField;
 15 //  下拉区id
 16 var  divName;
 17 //  IFrame名称
 18 var  ifName;
 19 //  记录上次选择的值
 20 var  lastVal  =   "" ;
 21 //  当前选择的值
 22 var  val  =   "" ;
 23 //  显示结果的下拉区
 24 var  globalDiv;
 25 //  下拉区是否设置格式的标记
 26 var  divFormatted  =   false ;
 27
 28 /**
 29InitQueryCode函数必须在<body onload>事件的响应函数中调用,其中:
 30queryFieldName为文本框控件的id,
 31hiddenDivName为显示下拉区div的id
 32*/

 33 function  InitQueryCode (queryFieldName, hiddenDivName)
 34 {
 35    // 指定文本输入框的onblur和onkeydown响应函数
 36    queryField = document.getElementById(queryFieldName);
 37    queryField.onblur = hideDiv;
 38    queryField.onkeydown = keypressHandler;
 39
 40    // 设置queryField的autocomplete属性为"off"
 41    queryField.autocomplete = "off";
 42
 43    // 如果没有指定hiddenDivName,取默认值"querydiv"
 44    if (hiddenDivName)
 45    {
 46        divName = hiddenDivName;
 47    }

 48    else
 49    {
 50        divName = "querydiv";
 51    }

 52    
 53    // IFrame的name
 54    ifName = "queryiframe";
 55    
 56    // 100ms后调用mainLoop函数
 57    setTimeout("mainLoop()"100);
 58}

 59
 60 /**
 61获取下拉区的div,如果没有则创建之
 62*/

 63 function  getDiv (divID)
 64 {
 65    if (!globalDiv)
 66    {
 67        // 如果div在页面中不存在,创建一个新的div
 68        
 69        if (!document.getElementById(divID))
 70        {
 71            var newNode = document.createElement("div");
 72            newNode.setAttribute("id", divID);
 73            document.body.appendChild(newNode);
 74        }

 75
 76        // globalDiv设置为div的引用        
 77        globalDiv = document.getElementById(divID);
 78
 79        // 计算div左上角的位置        
 80        var x = queryField.offsetLeft;
 81        var y = queryField.offsetTop + queryField.offsetHeight;
 82        var parent = queryField;
 83        while (parent.offsetParent)
 84        {
 85            parent = parent.offsetParent;
 86            x += parent.offsetLeft;
 87            y += parent.offsetTop;
 88        }

 89
 90        // 如果没有对div设置格式,则为其设置相应的显示样式        
 91        if (!divFormatted)
 92        {
 93            globalDiv.style.backgroundColor = DIV_BG_COLOR;
 94            globalDiv.style.fontFamily = DIV_FONT;
 95            globalDiv.style.padding = DIV_PADDING;
 96            globalDiv.style.border = DIV_BORDER;
 97            globalDiv.style.width = "100px";
 98            globalDiv.style.fontSize = "90%";
 99
100            globalDiv.style.position = "absolute";
101            globalDiv.style.left = x + "px";
102            globalDiv.style.top = y + "px";
103            globalDiv.style.visibility = "hidden";
104            globalDiv.style.zIndex = 10000;
105
106            divFormatted = true;
107        }

108    }

109
110    return globalDiv;
111}

112
113 /**
114根据返回的结果集显示下拉区
115*/

116 function  showQueryDiv(resultArray)
117 {
118    // 获取div的引用
119    var div = getDiv(divName);
120    
121    // 如果div中有内容,则删除之
122    while (div.childNodes.length > 0)
123        div.removeChild(div.childNodes[0]);
124
125    // 依次添加结果
126    for (var i = 0; i < resultArray.length; i++)
127    {
128        // 每一个结果也是一个div
129        var result = document.createElement("div");
130        // 设置结果div的显示样式
131        result.style.cursor = "pointer";
132        result.style.padding = "2px 0px 2px 0px";
133        // 设置为未选中
134        _unhighlightResult(result);
135        // 设置鼠标移进、移出等事件响应函数
136        result.onmousedown = selectResult;
137        result.onmouseover = highlightResult;
138        result.onmouseout = unhighlightResult;
139
140        // 结果的文本是一个span
141        var result1 = document.createElement("span");
142        // 设置文本span的显示样式
143        result1.className = "result1";
144        result1.style.textAlign = "left";
145        result1.style.fontWeight = "bold";
146        result1.innerHTML = resultArray[i];
147        
148        // 将span添加为结果div的子节点
149        result.appendChild(result1);
150        
151        // 将结果div添加为下拉区的子节点
152        div.appendChild(result);
153    }

154
155    // 如果结果集不为空,则显示,否则不显示
156    showDiv(resultArray.length > 0);
157}

158
159 /**
160用户点击某个结果时,将文本框的内容替换为结果的文本,
161并隐藏下拉区
162*/

163 function  selectResult()
164 {
165    _selectResult(this);
166}

167
168 //  选择一个条目
169 function  _selectResult(item)
170 {
171    var spans = item.getElementsByTagName("span");
172    if (spans)
173    {
174        for (var i = 0; i < spans.length; i++)
175        {
176            if (spans[i].className == "result1")
177            {
178                queryField.value = spans[i].innerHTML;
179                lastVal = val = escape(queryField.value);
180                mainLoop();
181                queryField.focus();
182                showDiv(false);
183                return;
184            }

185        }

186    }

187}

188
189 /**
190当鼠标移到某个条目之上时,高亮显示该条目
191*/

192 function  highlightResult()
193 {
194    _highlightResult(this);
195}

196
197 function  _highlightResult(item)
198 {
199    item.style.backgroundColor = DIV_HIGHLIGHT_COLOR;
200}

201
202 /**
203当鼠标移出某个条目时,正常显示该条目
204*/

205 function  unhighlightResult()
206 {
207    _unhighlightResult(this);
208}

209
210 function  _unhighlightResult(item)
211 {
212    item.style.backgroundColor = DIV_BG_COLOR;
213}

214
215 /**
216显示/不显示下拉区
217*/

218 function  showDiv (show)
219 {
220    var div = getDiv(divName);
221    if (show)
222    {
223        div.style.visibility = "visible";
224    }

225    else
226    {
227        div.style.visibility = "hidden";
228    }

229    //adjustiFrame();
230}

231
232 /**
233隐藏下拉区
234*/

235 function  hideDiv ()
236 {
237    showDiv(false);
238}

239
240 /**
241调整IFrame的位置,这是为了解决div可能会显示在输入框后面的问题
242*/

243 function  adjustiFrame()
244 {
245    // 如果没有IFrame,则创建之
246    if (!document.getElementById(ifName))
247    {
248        var newNode = document.createElement("iFrame");
249        newNode.setAttribute("id", ifName);
250        newNode.setAttribute("src""javascript:false;");
251        newNode.setAttribute("scrolling""no");
252        newNode.setAttribute("frameborder""0");
253        document.body.appendChild(newNode);
254    }

255
256    iFrameDiv = document.getElementById(ifName);
257    var div = getDiv(divName);
258
259    // 调整IFrame的位置与div重合,并在div的下一层  
260    try
261    {
262        iFrameDiv.style.position = "absolute";
263        iFrameDiv.style.width = div.offsetWidth;
264        iFrameDiv.style.height = div.offsetHeight;
265        iFrameDiv.style.top = div.style.top;
266        iFrameDiv.style.left = div.style.left;
267        iFrameDiv.style.zIndex = div.style.zIndex - 1;
268        iFrameDiv.style.visibility = div.style.visibility;
269    }

270    catch (e)
271    {
272    }

273}

274
275 /**
276文本输入框的onkeydown响应函数
277*/

278 function  keypressHandler (evt)
279 {
280    // 获取对下拉区的引用        
281    var div = getDiv(divName);
282    
283    // 如果下拉区不显示,则什么也不做        
284    if (div.style.visibility == "hidden")
285    {
286        return true;
287    }

288
289    // 确保evt是一个有效的事件    
290    if (!evt && window.event)
291    {
292        evt = window.event;
293    }

294    var key = evt.keyCode;
295
296    var KEYUP = 38;
297    var KEYDOWN = 40;
298    var KEYENTER = 13;
299    var KEYTAB = 9;
300    
301    // 只处理上下键、回车键和Tab键的响应        
302    if ((key != KEYUP) && (key != KEYDOWN) && (key != KEYENTER) && (key != KEYTAB))
303    {
304        return true;
305    }

306
307    var selNum = getSelectedSpanNum(div);
308    var selSpan = setSelectedSpan(div, selNum);
309    
310    // 如果键入回车和Tab,则选择当前选择条目    
311    if ((key == KEYENTER) || (key == KEYTAB))
312    {
313        if (selSpan)
314        {
315            _selectResult(selSpan);
316        }

317        evt.cancelBubble = true;
318        return false;
319    }

320    else //如果键入上下键,则上下移动选中条目
321    {
322        if (key == KEYUP)
323        {
324            selSpan = setSelectedSpan(div, selNum - 1);
325        }

326        if (key == KEYDOWN)
327        {
328            selSpan = setSelectedSpan(div, selNum + 1);
329        }

330        if (selSpan)
331        {
332            _highlightResult(selSpan);
333        }

334    }

335
336    // 显示下拉区
337    showDiv(true);
338    return true;
339}

340
341 /**
342获取当前选中的条目的序号
343*/

344 function  getSelectedSpanNum(div)
345 {
346    var count = -1;
347    var spans = div.getElementsByTagName("div");
348    if (spans)
349    {
350        for (var i = 0; i < spans.length; i++)
351        {
352            count++;
353            if (spans[i].style.backgroundColor != div.style.backgroundColor)
354            {
355                return count;
356            }

357        }

358    }

359
360    return -1;
361}

362
363 /**
364选择指定序号的结果条目
365*/

366 function  setSelectedSpan(div, spanNum)
367 {
368    var count = -1;
369    var thisSpan;
370    var spans = div.getElementsByTagName("div");
371    if (spans)
372    {
373        for (var i = 0; i < spans.length; i++)
374        {
375            if (++count == spanNum)
376            {
377                _highlightResult(spans[i]);
378                thisSpan = spans[i];
379            }

380            else
381            {
382                _unhighlightResult(spans[i]);
383            }

384        }

385    }

386
387    return thisSpan;
388}

389
    InitQueryCode 函数必须在页面的 onload 响应中执行,该函数最后调用 setTimeout 方法执行了 mainLoop 方法。注意, mainLoop 方法并没有在 lookup.js 中定义,必须在包含 lookup.js 文件的页面文件中增加该函数的定义。于此,我们就需要在Default.aspx页面上加入如下定义:
 1 < script language = " javascript "  src = " lookup.js " >< / script>
 2 < script language = " javascript " >
 3 mainLoop  =   function ()
 4 {
 5    val = escape(queryField.value);                
 6    if (lastVal != val)
 7    {                
 8        var response = _Default.GetSearchItems(val);
 9                    showQueryDiv(response.value);
10lastVal = val;
11    }
                
12        setTimeout('mainLoop()'100);
13        return true;
14    }

15      < / script>
    由上述代码可以看到 mainLoop 函数每隔 100ms 会执行一次,它会判断当前文本输入框的值和上次提交查询的值是否相同,如果不同,它会重新向服务器发送请求进行查询,并且更新下拉区域的显示。
    于此,一个基于 Ajax的自动完成功能就实现了。

本文借鉴于《 ajax web2.0快速入门与项目实践》。
这本书上还使用了控件将该功能进行了封装,这样要实现Ajax的自动完成功能就更加方便了。在此就不做过多解说。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值