最火下载站首页
手机版
最火下载站
关注公众号
最火下载站

当前位置:首页 > 网络知识 > Web前端 > Ajax/JavaScript> 实例代码教程:一个基于AJAX的自动完成

实例代码教程:一个基于AJAX的自动完成

文章作者:网友投稿 发布时间:2009-03-16 来源:网络

  烈火建站网(LieHuo.Net)技术文档 我想大家在访问某些网站的时候都曾见到过基于Ajax的自动完成功能,比如:http://www.google.com?输入:烈火上,还没输入完毕,下面就有了:烈火上网导航。如下图: ?   一、引出Ajax的自动完成   现在要实现一个员工信息

  烈火建站学院(LieHuo.Net)技术文档 我想大家在访问某些网站的时候都曾见到过基于Ajax的自动完成功能,比如:http://www.google.com 输入:烈火上,还没输入完毕,下面就有了:烈火上网导航。如下图:

在新窗口中预览图片_烈火建站学院(LIEHUO.NET) 

  一、引出Ajax的自动完成

  现在要实现一个员工信息查询的功能,即根据输入的名字检索员工的详细信息。这是一个简单的数据表查询,在ASP.NET中实现这样的功能是比较简单的.

 

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

  二、自动完成功能的实现
     实现这样的功能需要按以下的步骤进行。

   服务器端提供GetSearchItems方法给客户端,用来返回满足条件的员工列表。
   客户端的输入框需要增加onkeydown响应函数,以便即时获取满足条件的员工列表。
   通过客户端的JavaScript动态列出待选结果的列表,同时还要提供键盘和鼠标的响应。

  三、服务器端实现

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

    在页面后台代码(Default.aspx.cs)的Page_Load方法中增加下面的代码:


以下为引用的内容:
protected void Page_Load(object sender, EventArgs e)
{
        AjaxPro.Utility.RegisterTypeForAjax(typeof(_Default));
}


    下面定义提供给客户端调用的方法GetSearchItems(),参数query为模糊查询的关键字值:

以下为引用的内容:
    [AjaxPro.AjaxMethod()]
    public ArrayList GetSearchItems(string query)
    {
        ArrayList items = new ArrayList();
        StringBuilder queryString = new StringBuilder();
        queryString.Append("select employeeid,lastname,firstname,title,titleofcourtesy from dbo.Employees");
        queryString.Append(" where firstname like '%" + query + "%'");

        DataSet ds = DataBase.Instance.ReturnDataSet(queryString.ToString());
        for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
        {
            items.Add(ds.Tables[0].Rows[i][2].ToString());
        }
        return items;
    }

  GetSearchItems方法返回一个ArrayList对象,它将包含所有以用户输入字符串的员工名字。 #p#副标题#e#

  四、客户端实现

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

   除了显示待选的结果之外,下拉区域还要对键盘、鼠标事件做出响应;为了实时地显示待选结果,还需要定时更新待选结果的列表。这些功能都封装在lookup.js中。下面是lookeu.js的定义:


以下为引用的内容:
// 下拉区背景色
var DIV_BG_COLOR = "#EEE";
// 高亮显示条目颜色
var DIV_HIGHLIGHT_COLOR = "#C30";
// 字体
var DIV_FONT = "Arial";
// 下拉区内补丁大小
var DIV_PADDING = "2px";
// 下拉区边框样式
var DIV_BORDER = "1px solid #CCC";


// 文本输入框
var queryField;
// 下拉区id
var divName;
// IFrame名称
var ifName;
// 记录上次选择的值
var lastVal = "";
// 当前选择的值
var val = "";
// 显示结果的下拉区
var globalDiv;
// 下拉区是否设置格式的标记
var divFormatted = false;

/**
InitQueryCode函数必须在

 // 设置queryField的autocomplete属性为"off"
 queryField.autocomplete = "off";

 // 如果没有指定hiddenDivName,取默认值"querydiv"
 if (hiddenDivName)
 {
  divName = hiddenDivName;
 }
 else
 {
  divName = "querydiv";
 }
 
 // IFrame的name
 ifName = "queryiframe";
 
 // 100ms后调用mainLoop函数
 setTimeout("mainLoop()", 100);
}

/**
获取下拉区的div,如果没有则创建之
*/
function getDiv (divID)
{
 if (!globalDiv)
 {
  // 如果div在页面中不存在,创建一个新的div
  
  if (!document.getElementById(divID))
  {
   var newNode = document.createElement("div");
   newNode.setAttribute("id", divID);
   document.body.appendChild(newNode);
  }

  // globalDiv设置为div的引用  
  globalDiv = document.getElementById(divID);

  // 计算div左上角的位置  
  var x = queryField.offsetLeft;
  var y = queryField.offsetTop + queryField.offsetHeight;
  var parent = queryField;
  while (parent.offsetParent)
  {
   parent = parent.offsetParent;
   x += parent.offsetLeft;
   y += parent.offsetTop;
  }

  // 如果没有对div设置格式,则为其设置相应的显示样式  
  if (!divFormatted)
  {
   globalDiv.style.backgroundColor = DIV_BG_COLOR;
   globalDiv.style.fontFamily = DIV_FONT;
   globalDiv.style.padding = DIV_PADDING;
   globalDiv.style.border = DIV_BORDER;
   globalDiv.style.width = "100px";
   globalDiv.style.fontSize = "90%";

   globalDiv.style.position = "absolute";
   globalDiv.style.left = x + "px";
   globalDiv.style.top = y + "px";
   globalDiv.style.visibility = "hidden";
   globalDiv.style.zIndex = 10000;

   divFormatted = true;
  }
 }

 return globalDiv;
}

/**
根据返回的结果集显示下拉区
*/
function showQueryDiv(resultArray)
{
 // 获取div的引用
 var div = getDiv(divName);
 
 // 如果div中有内容,则删除之
 while (div.childNodes.length > 0)
  div.removeChild(div.childNodes[0]);

 // 依次添加结果
 for (var i = 0; i < resultArray.length; i++)
 {
  // 每一个结果也是一个div
  var result = document.createElement("div");
  // 设置结果div的显示样式
  result.style.cursor = "pointer";
  result.style.padding = "2px 0px 2px 0px";
  // 设置为未选中
  _unhighlightResult(result);
  // 设置鼠标移进、移出等事件响应函数
  result.onmousedown = selectResult;
  result.onmouseover = highlightResult;
  result.onmouseout = unhighlightResult;

  // 结果的文本是一个span
  var result1 = document.createElement("span");
  // 设置文本span的显示样式
  result1.className = "result1";
  result1.style.textAlign = "left";
  result1.style.fontWeight = "bold";
  result1.innerHTML = resultArray[i];
  
  // 将span添加为结果div的子节点
  result.appendChild(result1);
  
  // 将结果div添加为下拉区的子节点
  div.appendChild(result);
 }

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

/**
用户点击某个结果时,将文本框的内容替换为结果的文本,
并隐藏下拉区
*/
function selectResult()
{
 _selectResult(this);
}

// 选择一个条目
function _selectResult(item)
{
 var spans = item.getElementsByTagName("span");
 if (spans)
 {
  for (var i = 0; i < spans.length; i++)
  {
   if (spans[i].className == "result1")
   {
    queryField.value = spans[i].innerHTML;
    lastVal = val = escape(queryField.value);
    mainLoop();
    queryField.focus();
    showDiv(false);
    return;
   }
  }
 }
}

/**
当鼠标移到某个条目之上时,高亮显示该条目
*/
function highlightResult()
{
 _highlightResult(this);
}

function _highlightResult(item)
{
 item.style.backgroundColor = DIV_HIGHLIGHT_COLOR;
}

/**
当鼠标移出某个条目时,正常显示该条目
*/
function unhighlightResult()
{
 _unhighlightResult(this);
}

function _unhighlightResult(item)
{
 item.style.backgroundColor = DIV_BG_COLOR;
}

/**
显示/不显示下拉区
*/
function showDiv (show)
{
 var div = getDiv(divName);
 if (show)
 {
  div.style.visibility = "visible";
 }
 else
 {
  div.style.visibility = "hidden";
 }
 //adjustiFrame();
}

/**
隐藏下拉区
*/
function hideDiv ()
{
 showDiv(false);
}

/**
调整IFrame的位置,这是为了解决div可能会显示在输入框后面的问题
*/
function adjustiFrame()
{
 // 如果没有IFrame,则创建之
 if (!document.getElementById(ifName))
 {
  var newNode = document.createElement("iFrame");
  newNode.setAttribute("id", ifName);
  newNode.setAttribute("src", "javascript:false;");
  newNode.setAttribute("scrolling", "no");
  newNode.setAttribute("frameborder", "0");
  document.body.appendChild(newNode);
 }

 iFrameDiv = document.getElementById(ifName);
 var div = getDiv(divName);

 // 调整IFrame的位置与div重合,并在div的下一层 
 try
 {
  iFrameDiv.style.position = "absolute";
  iFrameDiv.style.width = div.offsetWidth;
  iFrameDiv.style.height = div.offsetHeight;
  iFrameDiv.style.top = div.style.top;
  iFrameDiv.style.left = div.style.left;
  iFrameDiv.style.zIndex = div.style.zIndex - 1;
  iFrameDiv.style.visibility = div.style.visibility;
 }
 catch (e)
 {
 }
}

/**
文本输入框的onkeydown响应函数
*/
function keypressHandler (evt)
{
 // 获取对下拉区的引用  
 var div = getDiv(divName);
 
 // 如果下拉区不显示,则什么也不做  
 if (div.style.visibility == "hidden")
 {
  return true;
 }

 // 确保evt是一个有效的事件 
 if (!evt && window.event)
 {
  evt = window.event;
 }
 var key = evt.keyCode;

 var KEYUP = 38;
 var KEYDOWN = 40;
 var KEYENTER = 13;
 var KEYTAB = 9;
 
 // 只处理上下键、回车键和Tab键的响应  
 if ((key != KEYUP) && (key != KEYDOWN) && (key != KEYENTER) && (key != KEYTAB))
 {
  return true;
 }

 var selNum = getSelectedSpanNum(div);
 var selSpan = setSelectedSpan(div, selNum);
 
 // 如果键入回车和Tab,则选择当前选择条目 
 if ((key == KEYENTER) || (key == KEYTAB))
 {
  if (selSpan)
  {
   _selectResult(selSpan);
  }
  evt.cancelBubble = true;
  return false;
 }
 else //如果键入上下键,则上下移动选中条目
 {
  if (key == KEYUP)
  {
   selSpan = setSelectedSpan(div, selNum - 1);
  }
  if (key == KEYDOWN)
  {
   selSpan = setSelectedSpan(div, selNum + 1);
  }
  if (selSpan)
  {
   _highlightResult(selSpan);
  }
 }

 // 显示下拉区
 showDiv(true);
 return true;
}

/**
获取当前选中的条目的序号
*/
function getSelectedSpanNum(div)
{
 var count = -1;
 var spans = div.getElementsByTagName("div");
 if (spans)
 {
  for (var i = 0; i < spans.length; i++)
  {
   count++;
   if (spans[i].style.backgroundColor != div.style.backgroundColor)
   {
    return count;
   }
  }
 }

 return -1;
}

/**
选择指定序号的结果条目
*/
function setSelectedSpan(div, spanNum)
{
 var count = -1;
 var thisSpan;
 var spans = div.getElementsByTagName("div");
 if (spans)
 {
  for (var i = 0; i < spans.length; i++)
  {
   if (++count == spanNum)
   {
    _highlightResult(spans[i]);
    thisSpan = spans[i];
   }
   else
   {
    _unhighlightResult(spans[i]);
   }
  }
 }

 return thisSpan;
}


    InitQueryCode函数必须在页面的onload响应中执行,该函数最后调用setTimeout方法执行了mainLoop方法。注意,mainLoop方法并没有在lookup.js中定义,必须在包含lookup.js文件的页面文件中增加该函数的定义。于此,我们就需要在Default.aspx页面上加入如下定义:


以下为引用的内容:
<script language="javascript" src="lookup.js"></script><script language="javascript">ainLoop = function(){ val = escape(queryField.value); if (lastVal != val){ var response = _Default.GetSearchItems(val); showQueryDiv(response.value); lastVal = val; } setTimeout('mainLoop()', 100); return true; }</script>


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

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

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

上一篇: JavaScript框架工具JavaScriptMVC 1.5

下一篇: 用JavaScript+CSS网站导航菜单的制作

共有2条评论网友评论

最新评论

  • 1楼 2019-12-17 17:27
    好玩的聊天社交软件合集评论
  • 2楼 2019-12-18 11:17
    123123