惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

S
Securelist
L
LangChain Blog
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
腾讯CDC
月光博客
月光博客
S
Schneier on Security
Simon Willison's Weblog
Simon Willison's Weblog
WordPress大学
WordPress大学
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
D
Darknet – Hacking Tools, Hacker News & Cyber Security
C
Cisco Blogs
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
美团技术团队
S
Security @ Cisco Blogs
人人都是产品经理
人人都是产品经理
L
LINUX DO - 热门话题
S
SegmentFault 最新的问题
D
DataBreaches.Net
C
CXSECURITY Database RSS Feed - CXSecurity.com
Microsoft Security Blog
Microsoft Security Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
N
News | PayPal Newsroom
W
WeLiveSecurity
F
Fortinet All Blogs
The Hacker News
The Hacker News
The Register - Security
The Register - Security
P
Palo Alto Networks Blog
Engineering at Meta
Engineering at Meta
T
The Exploit Database - CXSecurity.com
Vercel News
Vercel News
G
GRAHAM CLULEY
博客园 - 聂微东
P
Privacy International News Feed
P
Privacy & Cybersecurity Law Blog
Hacker News: Ask HN
Hacker News: Ask HN
Webroot Blog
Webroot Blog
G
Google Developers Blog
T
Tailwind CSS Blog
V
Vulnerabilities – Threatpost
L
Lohrmann on Cybersecurity
T
Tenable Blog
Cisco Talos Blog
Cisco Talos Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
Security Affairs
云风的 BLOG
云风的 BLOG
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
B
Blog
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Hugging Face - Blog
Hugging Face - Blog
T
Threatpost

博客园 - woodfish

偏序集的Dilworth定理 XJOJ历经2个月终于完成啦~~ fork()调用的一个趣题 POJ 3691 安徽第二题 有限状态自动机+DP 哈尔滨赛区网络预选赛总结 [计算几何]点集中的点能组成多少个正方形 [计算几何]POJ 1375 点对圆的切线+线段重叠 [计算几何]POJ 1556 判断线段相交+Dijkstra [计算几何] POJ 1873 暴力+凸包 [计算几何]POJ 1266 三角形的外接圆 圆的参数方程 POJ 1026 置换群 [计算几何]POJ 1031 计算点对多边形的偏转角度 [计算几何]POJ2079 求点集中面积最大的三角形 [计算几何]POJ3608 求2个不相交凸包的最短距离 [计算几何]凸包的旋转卡壳算法 C++禁止一个类被继承的技术 C与汇编的接口技术 计算位数的3种方法 避免使用条件分支
实现google那种输入框提示的功能
woodfish · 2008-10-18 · via 博客园 - woodfish

     今天在项目中遇到一个需求,就是在input输入框中录入数据时(名字),如果敲入了一部分,在数据库中存在已有的姓名的前缀与之匹配,就弹出一个 google页面那种效果的下拉框,把相关的姓名列出来供他选择。可以用箭头上下选择,也可以用鼠标选择。这样可以提高录入的效率。

    输入框的布局如下:

   

  最终的效果如下:

 

  表单中输入框的html代码为:

<tr><td>发件人</td><td><input type="text" name="sendername" id="sendername" value=""/></td></tr>

为了在输入一些内容时,能够显示出那个选择框,我们要建立一个div,初始时不可见,当需要时就显示出来,并且将它移动到正确的位置。这个div采用的定位为绝对定位,这样就可以在整个页面用left和top来定位了,z-index为99,这样就可以盖住下面的东西。选择框的div为:

<div id="helper" class="gac_m" style="visibility:hidden;" width="100%">
</div>

 gac_m的css定义为:

.gac_m {
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正下方:

function getAbsPosition(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中。

function checkvalue() {
    
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定义为:

.gac_b {
background
:#3366CC !important;
color
:white;
}

下面需要解决的问题是用向上和向下的按键来在选择框中选择,这样就需要检测键盘按键了,可以直接通过document.onkeydown来注册按键检测函数,当按下向上和向下的键时,就上下移动选中的行,函数choosetr(nextid)用来选择第nextid行,当然需要记录nowid为前面选中的行,然后selecttr()是当按了空格键或者用鼠标点击了后模拟选中的动作,用选中的值来填充input。

var nowid = 0;
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;

        nowid = 0;
        
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));
}