转自:http://www.cnblogs.com/woodfish1988/archive/2008/10/18/1314221.html
今天在项目中遇到一个需求,就是在input输入框中录入数据时(名字),如果敲入了一部分,在数据库中存在已有的姓名的前缀与之匹配,就弹出一个 google页面那种效果的下拉框,把相关的姓名列出来供他选择。可以用箭头上下选择,也可以用鼠标选择。这样可以提高录入的效率。
输入框的布局如下:
最终的效果如下:
表单中输入框的html代码为:
为了在输入一些内容时,能够显示出那个选择框,我们要建立一个div,初始时不可见,当需要时就显示出来,并且将它移动到正确的位置。这个div采用的定位为绝对定位,这样就可以在整个页面用left和top来定位了,z-index为99,这样就可以盖住下面的东西。选择框的div为:
</ div >
gac_m的css定义为:
background : white none repeat scroll 0 0 ;
border : 1px solid black ;
cursor : default ;
font-size : 13px ;
line-height : 17px ;
margin : 0 ;
position : absolute ;
z-index : 99 ;
width : 100px ;
left : 10px ;
top : 10px ;
}
当需要显示时,我们需要将id为helper的div移动到正确的位置,这个位置就是input输入框的正下方,left即为input的相对页面左边的位移,top即为input相对页面上边的位移加上input本身的高度。现在的问题是怎么取得一个元素的相对页面的绝对位置,这个可以通过offsetParent取的父亲结点递归的来算。下面函数getAbsPosition用来取得元素obj的绝对位置,gettable(obj)用来把id为helper的div定位到obj正下方:
var r = {
left: obj.offsetLeft,
top : obj.offsetTop
};
r.left = obj.offsetLeft;
r.top = obj.offsetTop;
if (obj.offsetParent) {
var tmp = getAbsPosition(obj.offsetParent);
r.left += tmp.left;
r.top += tmp.top;
}
return r;
}
function gettable(obj) {
var pos = getAbsPosition(obj);
pos.top += obj.offsetHeight;
document.getElementById( ' helper ' ).style.top = pos.top + " px " ;
document.getElementById( ' helper ' ).style.left = pos.left + " px " ;
document.getElementById( ' helper ' ).style.width = obj.offsetWidth + " px " ;
document.getElementById( ' helper ' ).style.visibility = '' ;
}
现在可以定位并且显示id为helper的div了,下面的问题是当输入框中的内容改变时,采用Ajax去服务器取得数据,然后更新div中的内容,并且显示出来。那么怎么侦测输入框中的内容改变了呢?如果采用onchange事件,只有当输入框失去焦点时才会触发,所以我们只有采用定时器的方式了,每隔一个时间片检查一下输入框的值,如果值改变了,就执行更新过程。下面checkvalue函数就是用来给定时器的函数,用来检查输入框的内容是否改变,getNameResponse是Ajax的回调函数,用来对服务器传回的JSON数据解码并且以一定的方式显示在div中。
var nowvalue = document.getElementById( ' sendername ' ).value;
if (prevalue != nowvalue) {
Ajax.call( ' useradv.php?act=getsendername&name= ' + nowvalue, '' ,getNameResponse, ' GET ' , ' JSON ' );
}
prevalue = nowvalue;
setTimeout(checkvalue, 100 );
}
function getNameResponse(result) {
var info = result.content;
var html = ' <table width="100%"> ' ;
for ( var i = 0 ;i < info.length; i ++ ) {
html += ' <tr id="tr ' + (i + 1 ) + ' " onmouseover="choosetr( ' + (i + 1 ) + ' );" onclick="selecttr();"> '
+ ' <td colspan="2"> ' + info[i].sendername + ' </td></tr> ' ;
}
html += ' <td align="left"><font color="red">按空格键选择</font></td> '
+ ' <td align="right"><a href="#" onclick="document.getElementById(/ ' helper/ ' ).style.visibility=/ ' hidden/ ' ;"> ' +
' 关闭</a></td></tr></table> ' ;
document.getElementById( ' helper ' ).innerHTML = html;
if (info.length > 0 ) {
gettable(document.getElementById( ' sendername ' ));
} else {
document.getElementById( ' helper ' ).style.visibility = ' hidden ' ;
}
}
其中tr的className为gca_b表示该行被选中,其CSS定义为:
background : #3366CC !important ;
color : white ;
}
下面需要解决的问题是用向上和向下的按键来在选择框中选择,这样就需要检测键盘按键了,可以直接通过document.onkeydown来注册按键检测函数,当按下向上和向下的键时,就上下移动选中的行,函数choosetr(nextid)用来选择第nextid行,当然需要记录nowid为前面选中的行,然后selecttr()是当按了空格键或者用鼠标点击了后模拟选中的动作,用选中的值来填充input。
function selecttr() {
try
{
if (document.getElementById( ' helper ' ).style.visibility == ' hidden ' ) return ;
document.getElementById( ' sendername ' ).value =
document.getElementById( ' tr ' + nowid).cells[ 0 ].innerHTML;
setTimeout( function (){
document.getElementById('sendername').value
=Utils.trim(document.getElementById('sendername').value);},50);
falg = false;
var eles = document.getElementsByName( ' trname ' );
for ( var i = 0 ; i < eles.length; i ++ ) {
eles[i].className = '' ;
}
document.getElementById( ' helper ' ).style.visibility = ' hidden ' ;
}
catch (e)
{
}
}
document.onkeydown = function (e) {
var keycode;
try
{
keycode = event.keyCode;
}
catch (err)
{
keycode = e.keyCode;
}
if (keycode == 40 ) {
// 按了向下的键
choosetr(nowid + 1 );
} else if (keycode == 38 ) {
// 按了向上的键
choosetr(nowid - 1 );
} else if (keycode == 32 ) {
selecttr();
}
}
function choosetr(nextid) {
var len = 0 ;
do
{
try
{
var obj = document.getElementById( ' tr ' + (len + 1 ));
if (obj) len ++ ; else break ;
}
catch (e)
{
break ;
}
} while ( 1 );
if (nextid > len) nextid = 1 ;
if (nextid < 1 ) nextid = len;
if (nowid >= 1 && nowid <= len) {
document.getElementById( ' tr ' + nowid).className = '' ;
}
document.getElementById( ' tr ' + nextid).className = ' gac_b ' ;
nowid = nextid;
}
setTimeout(checkvalue, 100 );
这样就可以了,最后需要注意的是有个小问题:浏览器自带的自动历史选择下拉框会与我们做的冲突,解决方法就是设置input的oncomplete="off"。
最后贴一下服务器端的php程序:
elseif ( $action == ' getsendername ' ) {
$name = trim ( $_GET [ ' name ' ]);
$rows = array ();
if ( strlen ( $name ) > 0 ) {
$sql = " select distinct sendername from " . $GLOBALS [ ' ecs ' ] -> table( ' useradv ' ) .
" where sendername like ' $name %' and sendername != ' $name ' limit 0,10 " ;
$rows = $GLOBALS [ ' db ' ] -> getAll( $sql );
}
include_once (ROOT_PATH . ' includes/cls_json.php ' );
$json = new JSON();
$result = array ( ' error ' => 0 , ' message ' => '' , ' content ' => '' );
$result [ ' content ' ] = $rows ;
die ( $json -> encode( $result ));
}