利用回发 实现一个简单的AutoComplete功能
把前段时间做的Auto Complete功能做了下整理 与各位分享
由于我们项目尽量避免使用AJAX,所以我们不得不重新写了套东西来实现Auto Complete。 还是感觉做成控件好一点。下面我来说下具体实现。
基本原理就是利用回发机制,实现ICallbackEventHandler接口来达到页面不刷新 但是能与后台交换数据的功能。
ASPX
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Audto Complete</title>
<script src="autoComplete.js" type="text/javascript" language="javascript"></script>
</head>
<body>
<form id="form1" runat="server">
<div>
<script>
function GetServerMethod(controlID)
{
var message = controlID+","+document.getElementById(controlID).value;
var context = controlID;
<%=Page.ClientScript.GetCallbackEventReference(this, "message", "ShowAutoCompleteList", "context")%>
}
</script>
<asp:Label ID="lbl_Name" runat="server" Text="Product Name:"></asp:Label>
<asp:TextBox ID="txt_Name" runat="server" onkeyup="GetServerMethod(this.id);" autocomplete="off"></asp:TextBox>
</div>
</form>
</body>
</html>
<head runat="server">
<title>Audto Complete</title>
<script src="autoComplete.js" type="text/javascript" language="javascript"></script>
</head>
<body>
<form id="form1" runat="server">
<div>
<script>
function GetServerMethod(controlID)
{
var message = controlID+","+document.getElementById(controlID).value;
var context = controlID;
<%=Page.ClientScript.GetCallbackEventReference(this, "message", "ShowAutoCompleteList", "context")%>
}
</script>
<asp:Label ID="lbl_Name" runat="server" Text="Product Name:"></asp:Label>
<asp:TextBox ID="txt_Name" runat="server" onkeyup="GetServerMethod(this.id);" autocomplete="off"></asp:TextBox>
</div>
</form>
</body>
</html>
注意把TEXTBOX的autocomplete属性设为FALSE,不然会引起浏览器的自动填充功能。
我把对应要实现AutoComplete的控件ID和输入的值传到服务器端,message 保存ID和值,context保存ID,然后调用服务器方法。我们再来看下CS 文件
要实现接口 ICallbackEventHandler, 很简单
ICallbackEventHandler Members
我们来看看RenderList方法是怎么写的
/// <summary>
/// Fill the auto complete with datasource and argument passed from aspx.
/// </summary>
/// <param name="eventArgument">value passed from aspx</param>
/// <returns>a string contains all the html source used by auto complete</returns>
public string RenderList(string eventArgument)
{
string controlID = eventArgument.Split(',')[0];
string controlValue = eventArgument.Split(',')[1];
if (controlValue == string.Empty)
return string.Empty;
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < _dataSource.Count; i++)
{
if (_dataSource[i].ToString().StartsWith(controlValue, StringComparison.OrdinalIgnoreCase))
{
strBuilder.Append("<div onclick=""ValueFill(this, '" + controlID + "')"";'>");
strBuilder.Append(_dataSource[i].ToString());
strBuilder.Append("</div>");
}
}
return strBuilder.ToString();
}
/// Fill the auto complete with datasource and argument passed from aspx.
/// </summary>
/// <param name="eventArgument">value passed from aspx</param>
/// <returns>a string contains all the html source used by auto complete</returns>
public string RenderList(string eventArgument)
{
string controlID = eventArgument.Split(',')[0];
string controlValue = eventArgument.Split(',')[1];
if (controlValue == string.Empty)
return string.Empty;
StringBuilder strBuilder = new StringBuilder();
for (int i = 0; i < _dataSource.Count; i++)
{
if (_dataSource[i].ToString().StartsWith(controlValue, StringComparison.OrdinalIgnoreCase))
{
strBuilder.Append("<div onclick=""ValueFill(this, '" + controlID + "')"";'>");
strBuilder.Append(_dataSource[i].ToString());
strBuilder.Append("</div>");
}
}
return strBuilder.ToString();
}
主要就是通过控件上传来的值到服务器端的数据源中做比较 符合的就拿出来 生成到一个DIV中 最终生成一个符合的列表。
下面是写JS和样式
function ShowAutoCompleteList(retMessage, context)
{
var autoComplete=document.getElementById('AutoComplete');
if(retMessage=='')
{
Reset(); return;
}
autoComplete.className="AutoComplete";
autoComplete.style.display="block";
autoComplete.style.left=getX(document.getElementById(context))+"px";
autoComplete.style.top=getY(document.getElementById(context))+document.getElementById(context).offsetHeight+"px";
autoComplete.style.width=document.getElementById(context).offsetWidth+"px";
autoComplete.innerHTML=retMessage;
}
function ValueFill(obj,controlID)
{
document.getElementById(controlID).value=obj.innerHTML;
Reset();
}
function Reset()
{
document.getElementById('AutoComplete').className="AutoCompleteHide";
document.getElementById('AutoComplete').innerHTML="";
}
function getX(elem){
var x = elem.offsetLeft;
while(elem = elem.offsetParent){
x = x + elem.offsetLeft;
}
return x;
}
function getY(elem){
var y = elem.offsetTop;
while(elem = elem.offsetParent){
y = y + elem.offsetTop;
}
return y;
}
if (document.addEventListener)
{document.addEventListener('click',Reset, false);}
else
{document.attachEvent('onclick',Reset);}
{
var autoComplete=document.getElementById('AutoComplete');
if(retMessage=='')
{
Reset(); return;
}
autoComplete.className="AutoComplete";
autoComplete.style.display="block";
autoComplete.style.left=getX(document.getElementById(context))+"px";
autoComplete.style.top=getY(document.getElementById(context))+document.getElementById(context).offsetHeight+"px";
autoComplete.style.width=document.getElementById(context).offsetWidth+"px";
autoComplete.innerHTML=retMessage;
}
function ValueFill(obj,controlID)
{
document.getElementById(controlID).value=obj.innerHTML;
Reset();
}
function Reset()
{
document.getElementById('AutoComplete').className="AutoCompleteHide";
document.getElementById('AutoComplete').innerHTML="";
}
function getX(elem){
var x = elem.offsetLeft;
while(elem = elem.offsetParent){
x = x + elem.offsetLeft;
}
return x;
}
function getY(elem){
var y = elem.offsetTop;
while(elem = elem.offsetParent){
y = y + elem.offsetTop;
}
return y;
}
if (document.addEventListener)
{document.addEventListener('click',Reset, false);}
else
{document.attachEvent('onclick',Reset);}
/* AutoComplete Start */
.AutoComplete
{
float: left;
z-index: 1;
position: absolute;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-right-style: solid;
border-bottom-style: solid;
border-left-style: solid;
border-top-color: #D8D8D8;
border-right-color: #D8D8D8;
border-bottom-color: #D8D8D8;
border-left-color: #D8D8D8;
background-color: White;
}
.AutoCompleteHide
{
position: absolute;
visibility: hidden;
}
.AutoComplete div:hover
{
background-color: #00bbff;
cursor:default;
}
/* AutoComplete End */
.AutoComplete
{
float: left;
z-index: 1;
position: absolute;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-right-style: solid;
border-bottom-style: solid;
border-left-style: solid;
border-top-color: #D8D8D8;
border-right-color: #D8D8D8;
border-bottom-color: #D8D8D8;
border-left-color: #D8D8D8;
background-color: White;
}
.AutoCompleteHide
{
position: absolute;
visibility: hidden;
}
.AutoComplete div:hover
{
background-color: #00bbff;
cursor:default;
}
/* AutoComplete End */
问题
其实没啥大的难度,利用接口+JS+样式。不过我这个auto complete还不是个控件,还有在出发回发事件上还有点小问题。
我是在 onkeyup的时候做的异步回发,这就是说在控件获得焦点,每次按键都会和服务器交互,这显然会加重服务器端压力,这以后要改的。
还有的就是没有加入上下按键选择功能,用起来有点不方便。
各位有啥好的建议也不妨介绍下 谢谢