jQuery简单改进实现google suggest效果

【原文地址:jQuery实例:输入框下拉提示,仿google suggest
前言:最近项目中需要用到jQuery实现google suggest这种功能。搜了一下,除了jQuery插件,又粗略看了几篇直接手写jQuery代码的。发现开篇提到的“jQuery实例:输入框下拉提示,仿google suggest”这篇里的源代码被转载的较多,所以又认真研读了两遍。个人认为它的实现思路是非常清晰简单而且脚本代码量也是比较少的。不过,虽然楼猪看脚本是一点问题没有,但是在自己的电脑上实验的时候还是发现了比较严重的几个小问题。更可惜的是原文的服务端的实现和配置让楼猪感到了一点水土不服(它是针对java环境实现的)。经过自己努力,终于在.net环境下改进并扩展实现了这个功能,贴出来,希望对可能要用到的朋友有帮助。
1、效果图

2、实现思路
(1)、客户端(autoComplete.js)

var highlightindex = -1//定义高亮显示索引,-1代表不高亮任何行 
var timeOutId = null//定义延迟时间Id
var delayTime = 500//默认延迟0.5秒
var minPrefix = 0//定义最小几个字符开始搜索
var mouseOverCss = { background: "white", cursor: " pointer" }; //mouseover时样式
var mouseOutCss = { background: "white" }; //mouseout时样式
var grayCss = { background: "#cef", cursor: " pointer" };
var upDownGrayCss = { background: "#cef" };
var upDownWhiteCss = { background: "white" };

var ajaxProcessUrl = "AutoCompleteHandler.ashx"//发送ajax请求调用处理url

$(document).ready(
function() {
    
var wordInput = $("#txtQueryWord");
    
var wordInputOffset = wordInput.offset();

    
//隐藏自动补全框,并定义css属性
    $("#divAutoList").hide()
              .css(
"position""absolute")
              .css(
"border""1px black solid"//边框 黑色
              .css("top", wordInputOffset.top + wordInput.height() + 5 + "px")
              .css(
"left", wordInputOffset.left + "px")
              .width(wordInput.width() 
+ 2);


    
//给文本框添加键盘按下并弹起的事件
    wordInput.keyup(function(event) {
        
var myEvent = event || window.event;
        
var keyCode = myEvent.keyCode;
        
var autoNode = $("#divAutoList"); //可供选择的项

        
//        if (keyCode >= 65 && keyCode <= 90 || keyCode == 8 || keyCode == 46) { //输入字母,退格或删除,显示最新的内容
        if (keyCode != 13 && keyCode != 38 && keyCode != 40) { //不是三个特殊键,可以搜索
            var wordText = $("#txtQueryWord").val();
            
if (wordText.length < minPrefix) return;
            
//取消上次提交
            //window.clearTimeout(timeOutId);
            timeOutId = setTimeout(function() {
                
//ajax请求
                $.post(ajaxProcessUrl, { KeyWord: wordText }, function(data) {
                    
var jqueryObj = $(data);
                    
var wordNodes = jqueryObj.find("KeyWord"); //xml节点名
                    autoNode.html("");
                    wordNodes.each(
function(i) {
                        
var wrodNode = $(this);
                        
var newDivNode = $("<div>").attr("id", i);
                        newDivNode.html(wrodNode.text()).appendTo(autoNode); 
//xml文本内容( wrodNode.text() )
                        //添加光标进入事件, 高亮节点
                        newDivNode.mouseover(function() {
                            
if (highlightindex != -1) {
                                $(
"#divAutoList").children("div")
                                                            .eq(highlightindex)
                                                            .css(mouseOverCss);
                            }
                            highlightindex 
= $(this).attr("id");
                            $(
this).css(grayCss);
                        });

                        
//添加光标移出事件,取消高亮
                        newDivNode.mouseout(function() {
                            $(
this).css(mouseOutCss);
                        });

                        
//添加光标mousedown事件  点击事件newDivNode.click可能不起作用?
                        newDivNode.mousedown(function() {
                            
var comText = $(this).text();
                            $(
"#divAutoList").hide();
                            highlightindex 
= -1;
                            $(
"#txtQueryWord").val(comText);
                        });
                    });
                    
if (wordNodes.length > 0) {
                        autoNode.show();
                    }
                    
else {
                        autoNode.hide();
                        highlightindex 
= -1;
                    }

                }, 
"xml"); //xml结果集
            }, delayTime);
        }
        
else if (keyCode == 38) {//输入向上,选中文字高亮
            var autoNodes = $("#divAutoList").children("div")
            
if (highlightindex != -1) {
                autoNodes.eq(highlightindex).css(upDownWhiteCss);
                highlightindex
--;
            }
            
else {
                highlightindex 
= autoNodes.length - 1;
            }

            
if (highlightindex == -1) {
                highlightindex 
= autoNodes.length - 1;
            }
            autoNodes.eq(highlightindex).css(upDownGrayCss);
        }
        
else if (keyCode == 40) {//输入向下,选中文字高亮
            var autoNodes = $("#divAutoList").children("div")
            
if (highlightindex != -1) {
                autoNodes.eq(highlightindex).css(upDownWhiteCss);
            }
            highlightindex
++;
            
if (highlightindex == autoNodes.length) {
                highlightindex 
= 0;
            }
            autoNodes.eq(highlightindex).css(upDownGrayCss);

        }
        
else if (keyCode == 13) {//输入回车
            if (highlightindex != -1) {
                
var comText = $("#divAutoList").hide().children("div").eq(highlightindex).text();
                highlightindex 
= -1;
                $(
"#txtQueryWord").val(comText);
                
return false;
            }
            
else {
                alert(
"文本框中的[" + $("#txtQueryWord").val() + "]被提交了!");
                $(
"#divAutoList").hide();
                $(
"#txtQueryWord").get(0).blur();
                
return true;
            }
        }
    });

    
//给查询框添加blur事件,隐藏提示层
    $("#txtQueryWord").blur(function() {
        $(
"#divAutoList").hide();
    });

});

 总体思路:(1)给查询输入框附加keyup事件,触发事件时,后台处理ajax请求,返回xml格式的数据集;(2)、前台定义一个公共div,将返回的xml结果集合构造成可选择的子div(原文中的补全框);(3)、处理子div的几种事件和css效果,还有上下键和回车键功能。
对(1)和(3),楼猪对源代码进行了部分改进,对照源代码,您可以发现它们的变化。
(2)、服务端(AutoCompleteHandler.ashx)

代码
using System;
using System.Web;
using System.Text;
using System.Threading;

namespace WebTest
{

    
public class AutoCompleteHandler : IHttpHandler
    {

        
//原文:http://www.cnblogs.com/woodyy/archive/2010/02/03/1662370.html
        public void ProcessRequest(HttpContext context)
        {
            
string keyWord = string.Empty;
            
if (!string.IsNullOrEmpty(context.Request["KeyWord"]))
            {
                keyWord 
= context.Request["KeyWord"].Trim();
                ProcessRequest(context, keyWord);
            }
        }

        
private void ProcessRequest(HttpContext context, string keyWord)
        {
            
try
            {
                context.Response.ClearContent();
                context.Response.ContentType 
= ("text/xml;charset=UTF-8"); // 这里必须要设置,否则客户端接收不到这里写好的xml文件
                context.Response.Cache.SetCacheability(HttpCacheability.NoCache); //没有缓存
                string result = CreateXmlResult(keyWord);
                context.Response.Write(result);
                context.Response.Flush();
                context.Response.End();
            }
            
catch (ThreadAbortException ex)//这个异常不用处理
            {
                
string err = ex.Message;
            }
            
catch (Exception ex)
            {
                
string err = ex.Message;
                
//Logger.WriteFileLog(ex, Config.logDir);
            }
        }

        
/// <summary>
        
/// 生成返回提示结果对应的xml文件(实际编程重点关注这里就可以了)
        
/// </summary>
        
/// <param name="keyWord">实际项目中,应该使用返回的结果集,如泛型集合等等</param>
        
/// <returns></returns>
        private string CreateXmlResult(string keyWord)
        {
            StringBuilder sb 
= new StringBuilder(300);
            sb.Append(
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>");
            sb.Append(
"<Results>");
            
for (int i = 0; i < 10; i++)
            {
                sb.AppendFormat(
"<KeyWord>{0}_{1}</KeyWord>", keyWord, i);
            }
            sb.Append(
"</Results>");
            
return sb.ToString().Trim();
        }


        
public bool IsReusable
        {
            
get
            {
                
return false;
            }
        }
    }
}

 如您所看到的,在后台的处理,非常重要的就是CreateXmlResult方法,实际项目中,很可能会根据项目缓存策略的不同需求,读缓存的,访问数据库的,两种结合的等等,组合出搜索结果反馈给前台。 需要特别说明的是(对于有实际开发经验的应该算是常识),google suggest效果三种常见实现方式通常都需要配合后台处理ajax请求(当然也有不需要后台的,就是前台脚本控制会比较多,比如JQuery实现智能输入提示(仿机票预订网站)),服务端的实现也是很重要的(不论是ashx还是aspx实现),比如本文给出的后台实现方式(也许这篇对初学者会有用)。如果您尝试着在后台返回json数据格式,切记前台jQuery代码处理ajax请求的callback部分也需要改动。
 
好了,到这里,我们来总结一下几种常见的实现google suggest前台效果的方式:
1、纯js脚本
  编写大量javascript脚本,配合使用div或iframe实现(如楼猪早期的这篇,但是有两个重大缺陷:a、无法上下键选择;b、选择某一项文本后无法取到该文本text下所对应的value(注意:不是选择不到text啊,你懂的)。缺少这两种功能的任何一种,还能叫完整的google suggest么? o(╯□╰)o),虽然实现的效果和功能现在看来并不怎么华丽,可脚本量是非常可观的,而且楼猪一直认为网上这种玩转js的主都是纯爷们,令人不得不由衷地佩服和膜拜啊。
  但是,根据楼猪的实际开发和维护经历,每次看到几百行甚至上千行有若干函数的脚本文件,条件反射地会在脑袋里想起”哎呦我kao“的抱怨。个人认为,这种实现的特点而且也是它的缺点就是代码量较大,要考虑到的功能点非常繁杂,不利于维护。

2、使用微软的ajax控件
  这个嘛,还是要提一下的。在实际的项目中,楼猪也曾使用过直接拖拽ajax控件加webservice实现。使用起来那真的是如鱼得水滋润的不得了,尤其是取text对应value功能,不用花费多大精力写脚本,而且容易维护和扩展。

3、jQuery实现
  有现成的jQuery插件,也可以自己编写代码以适用于项目需要。相比较1的方法,个人认为利用jQuery真的省了不少精力,不知道大家实际开发的时候怎么样,反正楼猪是不复当年勇,现在再写很多脚本感觉有点力不从心了。

最后,由于楼猪慵懒惯了,先对自己提出三个建议功能督促自己:
a、做成用户控件,一个页面可以同时多次反复使用(demo里有涉及,没有完成);
b、选择某一项文本后无法取到该文本text下所对应的value;
c、css和后台处理请求url可配置(用户控件会朝着这个方向努力)。
楼猪正在考虑完善上面提到的功能要求,也欢迎大家的建议和意见,让我们一起把google suggest这个功能做的更好。

demo下载:jQuerySuggest

posted on 2010-04-17 20:09  JeffWong  阅读(8694)  评论(17编辑  收藏  举报