百度、谷歌的搜索提示是一个很cool的效果,恰逢美大(@司徒正美)前几天做了一个demo,于是自己也来实现一番,实现的思路有别于美大,可以对比着研究。
传送门——美大实现版:javascript suggest效果(mass framewrok很强大,强烈建议关注)
@个人实现效果(没有用框架...同时懒得做IE678的兼容...的草稿版,chrome下效果尤佳),实际效果也是查询当前浏览器的对象和属性:
↑_怒击链接下载实例
低调的声明:
1.由于是用原生js写的,而且没有做兼容,IE下不要指望正常了(= = !),有兴趣的话可以按思路用框架封装(广告:推荐司徒大大的mass框架)。
2.这里重点只表现出实现的思路。兼容性和代码组织、细节、性能优化等等忽略了。
效果比较简单,有几点供大家思考:
1.数据的查询方式可以通过search函数改变(如改成ajax)。
2.按键即触发查询,按键事件是绑定在keyup而不是keypress。
3.按上下键选择suggest条目的按键事件是绑定在keydown而不是keypress。
4.suggest条目较多时出现滚动条,由于按上下键选择suggest条时焦点在input,所以需要修正可视区域。
5.suggest框的显隐时机。
html结构:
输入框input,suggest框ul
<div id="warpper">
<input type="text" id="search" autocomplete="off" />
<ul id="suggest"></ul>
</div>
#warpper{
position:relative;
}
#search{
width:400px;
height:20px;
}
#suggest{
position:absolute;
top:30px;
width:400px;
max-height:400px;
overflow:auto;
display:none;
border:1px solid #20815D;
list-style:none;margin:0;padding:0;
cursor:default;
}
.color{
background-color:#BDF400;
}
li{
margin:0;padding-left:5px;height:20px;
}
js部分:
1.search的实现:
通过该search可以实现其他的查询方式或查询其他的数据,只要把查询结果以数组形式赋给output即可。
这里的search实现的是查找当前浏览器的对象和属性。
1 // 查找函数,可以换成ajax请求服务器json
2 // 这个search函数只是用来寻找当前浏览器下的全局对象和变量
3 function search(val){
4 // 触发查找的时候先隐藏掉suggest,里面已经不是想要的信息了
5 ul.style.display = "none";
6 // 清空输出数组,用来填充新数据
7 output = [];
8 var matchObj = val.split('.');//每一个点号都代表一次对象的属性查找
9 if(matchObj.length>1){
10 // 第一次搜索是从全局对象window开始
11 var currentObj = window;
12 for(var i = 0,len = matchObj.length; i < len; i++){
13 //搜索window下的各级对象
14 if(!currentObj[matchObj[i]]){
15 break;
16 }
17 currentObj = currentObj[matchObj[i]];
18 }
19 var left = len - i;
20 if(left===1){
21 var rProp = new RegExp("^"+matchObj[i]);
22 for(var prop in currentObj){
23 if(rProp.test(prop)){
24 matchObj[len-1] = prop;
25 output.push(matchObj.join('.'));
26 }
27 }
28 }else if(left===0){
29 //正好匹配,其实可以不输出的。
30 output.push(val);
31 }
32 }else{
33 //搜索window下带val字符串开头的属性
34 var rProp = new RegExp("^"+val);
35 for(var prop in window){
36 if(rProp.test(prop)){
37 output.push(prop);
38 }
39 }
40 }
41 }
2.inupt上的按键事件(查询数据,弹出suggest框,选择suggest条目,改变input值等):
1 input.onkeyup = function(e){
2 if(e.keyCode !== 38 && e.keyCode !==40){
3 // 不是上下方向键,则进行搜索,这里的搜索函数可以自定义,或者写成ajax请求
4 search(this.value);
5 // 取得数据后就可以调用createBox()生成并显示suggest框了
6 createBox();
7 }
8 }
9 input.onkeydown = function(e){
10 //toggleColor();切换选中项颜色
11 //input.val = li.textcontent;
12 // 上下方向键,进行createBox或者对选项切换颜色
13 if(e.keyCode === 38 || e.keyCode ===40){
14 // 如果是在suggest显示的状态下按上下方向键,那么改变高亮状态和修正位置
15 if(ul.style.display==="block"){
16 for(var i = 0,len = liSet.length; i<len;i++){
17 if(liSet[i].className === 'color'){
18 break;
19 }
20 }
21 if(i===len){
22 // 如果没有高亮状态,那么定义第一个高亮
23 liSet[0].className = 'color';
24 input.value = liSet[0].textContent;
25 }else{
26 // 取消当前高亮元素的高亮状态
27 liSet[i].className = '';
28
29 if(e.keyCode === 40){//设置高亮,修正位置
30 i = ++i%len;
31 liSet[i].className = 'color';
32 fixScrollPos(i);
33 }else{//设置高亮,修正位置
34 i = (--i+len)%len;
35 liSet[i].className = 'color';
36 e.preventDefault();//阻止光标默认操作,使得光标一直在右边
37 fixScrollPos(i);
38 }
39 }
40 // 按上下键的时候还没有显示suggest,那么此时显示出来,并且不设置高亮
41 }else{
42 if(output.length){
43 // 删除高亮
44 for(var i = 0,len = liSet.length; i<len;i++){
45 liSet[i].className = '';
46 }
47 // 显示suggest
48 ul.style.display = "block";
49 }
50 }
51 }
52 }
3.绑定在ul上的事件(选中suggest条目,鼠标划过条目时高亮等)
1 // 定义suggest的行为
2 ul.onclick = function(e){
3 // 单击li的时候添加对应的文字到input里
4 if(e.target.nodeName.toLowerCase() === 'li') input.value = e.target.textContent;
5 }
6 ul.onmouseover = function(e){
7 // 定义鼠标划过时suggest的高亮
8 for(var i = 0,len = liSet.length; i<len;i++){
9 liSet[i].className = '';
10 }
11 if(e.target.nodeName.toLowerCase() === 'li') e.target.className = 'color';
12 }
4.suggest的消失时机
1 document.onclick = function(e){
2 // 此处定义suggest何时消失,点击非input和ul下拉框则隐藏掉suggest
3 if(e.target!==input&&e.target!==ul) ul.style.display = "none";
4 }
5.suggest条目过多显示滚动条时,对上下按键触发的滚动效果模拟(此时焦点在input需要另外对suggest可视区域位置进行修正)
1 // 修正滚动条位置
2 function fixScrollPos(pos){
3 var sTop = ul.scrollTop;//先取得可视区的上边缘高
4 var viewHeight = parseInt(document.defaultView.getComputedStyle(ul,null).height,10);//可视区的高度
5 var sBottom = sTop + viewHeight;//可视区底部距离到ul顶部的高度
6 var itemHeight = liSet[0].offsetHeight;//假设这里没有margin,如果有的话自己补上
7 var liPos = pos*itemHeight;//当前li距离ul顶部的高度
8
9 if( liPos >= sBottom){//选中的li在可视区下
10 ul.scrollTop = liPos - ( viewHeight - itemHeight );
11 }else if( liPos < sTop){//选中的li在可视区上
12 ul.scrollTop = liPos;
13 }
14 liSet[pos].className = 'color';
15 input.value = liSet[pos].textContent;
16 }
基本介绍完了,更详细的可以看源代码:
再次怒击链接下载实例