• 基于AJAX的自动完成


         我想大家在访问某些网站的时候都曾见到过基于Ajax的自动完成功能,比如http://www.google.com,www.56.com/.    


    一、引出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方法中增加下面的代码
    1protected void Page_Load(object sender, EventArgs e)
    2{
    3        AjaxPro.Utility.RegisterTypeForAjax(typeof(_Default));
    4}

        下面定义提供给客户端调用的方法GetSearchItems(),参数query为模糊查询的关键字值:
     1[AjaxPro.AjaxMethod()]
     2public 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)的概念,通过JavaScriptDOM创建一个新的层div,将ArrayList中的每一个条目都作为其子节点加入到div中,而每一个条目也被看作是一个div,其中具体的文本内容则是一个span对象。

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

     33function 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*/

     63function 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*/

    116function 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*/

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

    167
    168// 选择一个条目
    169function _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*/

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

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

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

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

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

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

    218function 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*/

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

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

    243function 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*/

    278function 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*/

    344function 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*/

    366function 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">
     3mainLoop = 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的自动完成功能就更加方便了。在此就不做过多解说。

    本文示例代码下载:AutoComplete.rar


    ---------------------------------------------------------------------------------------------------------
  • 相关阅读:
    汇编指令lodsb和stosb、lodsd和stosd
    编码查询
    CLD汇编指令
    Win32编程
    MessageBox
    windows 数据类型
    STL总结
    解析结构化异常处理(SEH)(第二部分)
    FS[XX]
    ShellCode入门(提取ShellCode)
  • 原文地址:https://www.cnblogs.com/beniao/p/1171515.html
Copyright © 2020-2023  润新知