自动完成autoComplete

花了点时间改写了一个autoComplete.js,在之前的项目里用jQuery写的一个函数,现在改成原生js面向对象的方式写,花了不少力气当然也从中学习到了很多,下面发出来分享。

这个autoComplete对象需要初始化并绑定在一个input上面,面向对象的思想是借鉴上一篇随笔里面的日历控件。

首先定义了该对象的一些属性:

 
  
autoComplete={
  pop_len: 10 , //自动提示显示的结果条数
  pop_cn:
' autoDis ' , //提示层样式
  hover_cn:
' cur ' ,//鼠标滑过提示层样式
  source:
' 13612345564|13825646464|13412236054|13012348564|13012345564|13012365564 ' .split( ' | ' ),

source是数据源,这里写的几个静态数据测试,需要的话可以通过ajax动态取数据回来然后再绑定对象,这块暂时留着以后再来完善。

接下来是初始化程序,主要是在页面里构建一个div提示,里面的数据在keyup事件再添加进去,并且返回这个对象便于之后绑定对象;

 
  
  init: function (){
  
this .setDom();
  
return this ;
  },

然后就是bind方法,这个方法主要绑定这个input对象,使他具有autoComplete对象,并且绑定了键盘up和down键值可以上下翻滚并选中,代码有注释,也比较简单。

代码
 
   
bind: function (x){
if (x.getAttribute( ' type ' ) != ' text ' || x.nodeName != ' INPUT ' )
return null ;
var self = this ;
x.onkeyup
= function (e){
e
= e || window.event;
var lis = self.pop.getElementsByTagName( ' li ' ),lens = self.pop.getElementsByTagName( ' li ' ).length,n = lens,temp;
if (e.keyCode == 38 ){ // 键盘up键被按下
if (self.pop.style.display != ' none ' ){
for ( var i = 0 ;i < lens;i ++ ){ // 遍历结果数据,判断是否被选中
if (lis[i].className)
temp
= i;
else
n
-- ;
}
if (n == 0 ){ // 如果没有被选中的li元素,则选中最后一个
lis[lens - 1 ].className = self.hover_cn;
this .value = lis[lens - 1 ].innerHTML;
}
else { // 如果有被选中的元素,则选择上一个元素并赋值给input
if (lis[temp] == lis[ 0 ]){ // 如果选中的元素是第一个孩子节点则跳到最后一个选中
lis[lens - 1 ].className = self.hover_cn;
this .value = lis[lens - 1 ].innerHTML;
lis[temp].className
= '' ;
}
else {
lis[temp
- 1 ].className = self.hover_cn;
this .value = lis[temp - 1 ].innerHTML;
lis[temp].className
= '' ;
}
}
}
else // 如果弹出层没有显示则执行插入操作,并显示弹出层
self.insert( this );
}
else if (e.keyCode == 40 ){ // down键被按下,原理同up键
if (self.pop.style.display != ' none ' ){
for ( var i = 0 ;i < lens;i ++ ){
if (lis[i].className)
temp
= i;
else
n
-- ;
}
if (n == 0 ){
lis[
0 ].className = self.hover_cn;
this .value = lis[ 0 ].innerHTML;
}
else {
if (lis[temp] == lis[lens - 1 ]){
lis[
0 ].className = self.hover_cn;
this .value = lis[ 0 ].innerHTML;
lis[temp].className
= '' ;
}
else {
lis[temp
+ 1 ].className = self.hover_cn;
this .value = lis[temp + 1 ].innerHTML;
lis[temp].className
= '' ;
}
}
}
else
self.insert(
this );
}
else // 如果按下的键既不是up又不是down那么直接去匹配数据并插入
self.insert( this );
};
x.onblur
= function (){ // 这个延迟处理是因为如果失去焦点的时候是点击选中数据的时候会发现先无法触发点击事件
setTimeout( function (){self.pop.style.display = ' none ' ;}, 300 );
};
return this ;
},

然后是setDom方法,主要功能是在初始化时就在页面构建弹出层以及为了在ie6下挡住select元素而加的iframe,当时写这个方法直接把定位也在这里写了,结果发现绑定多个input的时候就会出问题,后来把弹出层定位放倒inset方法里。

 

代码
 
   
setDom: function (){
var self = this ;
var dom = document.createElement( ' div ' ),frame = document.createElement( ' iframe ' ),ul = document.createElement( ' ul ' );
document.body.appendChild(dom);
with (frame){ // 用来在ie6下遮住select元素
setAttribute( ' frameborder ' , ' 0 ' );
setAttribute(
' scrolling ' , ' no ' );
style.cssText
= ' z-index:-1;position:absolute;left:0;top:0; '
}
with (dom){ // 对弹出层li元素绑定onmouseover,onmouseout
className = this .pop_cn;
appendChild(frame);
appendChild(ul);
onmouseover
= function (e){ // 在li元素还没有加载的时候就绑定这个方法,通过判断target是否是li元素进行处理
e = e || window.event;
var target = e.srcElement || e.target;
if (target.tagName == ' LI ' ){ // 添加样式前先把所有的li样式去掉,这里用的是一种偷懒的方式,没有单独写removeClass方法
for ( var i = 0 ,lis = self.pop.getElementsByTagName( ' li ' );i < lis.length;i ++ )
lis[i].className
= '' ;
target.className
= self.hover_cn; // 也没有写addClass方法,直接赋值了
}
};
onmouseout
= function (e){
e
= e || window.event;
var target = e.srcElement || e.target;
if (target.tagName == ' LI ' )
target.className
= '' ;
};
}
this .pop = dom;
},

最后是insert方法,用来匹配数据,如果有匹配到的数据就定位弹出层,显示。

代码
 
   
insert: function (self){
var bak = [],s,li = [],left = 0 ,top = 0 ,val = self.value;
for ( var i = 0 ,leng = this .source.length;i < leng;i ++ ){ // 判断input的数据是否与数据源里的数据一致
if ( !! val && val.length <= this .source[i].length && this .source[i].substr( 0 ,val.length) == val){
bak.push(
this .source[i]);
}
}
if (bak.length == 0 ){ // 如果没有匹配的数据则隐藏弹出层
this .pop.style.display = ' none ' ;
return false ;
}
// 这个弹出层定位方法之前也是用循环offsetParent,但发现ie跟ff下差别很大(可能是使用方式不当),所以改用这个getBoundingClientRect
left = self.getBoundingClientRect().left + document.documentElement.scrollLeft;
top
= self.getBoundingClientRect().top + document.documentElement.scrollTop + self.offsetHeight;
with ( this .pop){
style.cssText
= ' width: ' + self.offsetWidth + ' px; ' + ' position:absolute;left: ' + left + ' px;top: ' + top + ' px;display:none; ' ;
getElementsByTagName(
' iframe ' )[ 0 ].setAttribute( ' width ' ,self.offsetWidth);
getElementsByTagName(
' iframe ' )[ 0 ].setAttribute( ' height ' ,self.offsetHeight);
onclick
= function (e){
e
= e || window.event;
var target = e.srcElement || e.target;
if (target.tagName == ' LI ' )
self.value
= target.innerHTML;
this .style.display = ' none ' ;
};
}
s
= bak.length > this .pop_len ? this .pop_len:bak.length;
for ( var i = 0 ;i < s;i ++ )
li.push(
' <li> ' + bak[i] + ' </li> ' );
this .pop.getElementsByTagName( ' ul ' )[ 0 ].innerHTML = li.join( '' );
this .pop.style.display = ' block ' ;
}

使用方法:autoComplete.init().bind('input对象')

 

对于js高手来说这样的效果基本都不值得一提,但对于我这样jQuery用的多的人来说写原生js还是比较费力,当时项目里用的方法用jQuery写只花了半天不到,但写这个却用了差不多两天的时间,感叹自己的基础知识还是非常的薄弱啊,继续加油吧!

转载于:https://www.cnblogs.com/foot3188/archive/2010/09/30/1839538.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值