用js实现输入提示(自动完成)

以前也写过一个jQuery的这种插件,但是很多地方根本不用jQuery,这个功能也有很多其它库支持,但是为了用这个功能而加载很多js插件,这样效率明显下降了很多,而且这个东西平时也很常用,所以一决心自己写了个相对比较独立的。

完成有以下功能:

  • 输入字符会把以输入字符开头的提示出来。
  • 支持上下方向键选择提示选项,支持循环
  • 支持绑定一个数组提示,支持ajax传递输入框值请求数据。
  • 支持多个选择的dom元素一块绑定数据实现输入提示。各dom元素也可以单独绑定自己的数据实现输入提示,互不影响。
  • 支持中文。

首先是js的核心部分,其各部分都有较详细的说明,代码如下:

001 ;(function(window){
002 /* 插件开始 */
003 var autoComplete=function(o){
004 var handler=(function(){
005 var handler=function(e,o){ return new handler.prototype.init(e,o); };/* 为每个选择的dom都创建一个相对应的对象,这样选择多个dom时可以很方便地使用 */
006 handler.prototype={
007 e:null, o:null, timer:null, show:0, input:null, popup:null,
008 init:function(e,o){/* 设置初始对象 */
009 this.e=e, this.o=o,
010 this.input=this.e.getElementsByTagName(this.o.input)[0],
011 this.popup=this.e.getElementsByTagName(this.o.popup)[0],
012 this.initEvent();/* 初始化各种事件 */
013 },
014 match:function(quickExpr,value,source){/* 生成提示 */
015 var li = null;
016 for(var i in source){
017 if( value.length>0 && quickExpr.exec(source[i])!=null ){
018 li = document.createElement('li');
019 li.innerHTML = '<a href="javascript:;">'+source[i]+'</a>';
020 this.popup.appendChild(li);
021 }
022 }
023 if(this.popup.getElementsByTagName('a').length)
024 this.popup.style.display='block';
025 else
026 this.popup.style.display='none';
027 },
028 ajax:function(type,url,quickExpr,search){/* ajax请求远程数据 */
029 var xhr = window.ActiveXObject ? new ActiveXObject("Microsoft.XMLHTTP") : new XMLHttpRequest();
030 xhr.open(type,url,true);/* 同异步在此修改 */
031 xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
032 var that=this;
033 xhr.onreadystatechange = function(){
034 if(xhr.readyState==4) {
035 if(xhr.status==200) {
036 var data = eval(xhr.responseText);
037 that.match(quickExpr,search,data);/* 相同于成功的回调函数 */
038 }else{
039 alert("请求页面异常!");/* 请求失败 */
040 }
041 }
042 };
043 xhr.send(null);
044 },
045 fetch:function(ajax,search,quickExpr){
046 var that=this;
047 this.ajax(ajax.type,ajax.url+search,quickExpr,search);
048 },
049 initEvent:function(){/* 各事件的集合 */
050 var that=this;
051 this.input.onfocus = function(){
052 if(this.inputValue) this.value = this.inputValue;
053 var value=this.value, quickExpr=RegExp('^'+value,'i'), self=this;
054 var els = that.popup.getElementsByTagName('a');
055 if(els.length>0) that.popup.style.display = 'block';
056 that.timer=setInterval(function(){
057 if(value!=self.value){/* 判断输入内容是否改变,兼容中文 */
058 value=self.value;
059 that.popup.innerHTML='';
060 if(value!=''){
061 quickExpr=RegExp('^'+value);
062 if(that.o.source) that.match(quickExpr,value,that.o.source);
063 else if(that.o.ajax) that.fetch(that.o.ajax,value,quickExpr);
064 }
065 }
066 },200);
067 };
068 this.input.onblur = function(){/* 输入框添加事件 */
069 if(this.value!=this.defaultValue) this.inputValue = this.value;
070 clearInterval(that.timer);
071 var current=-1;/* 记住当前有焦点的选项 */
072 var els = that.popup.getElementsByTagName('a');
073 var len = els.length-1;
074 var aClick = function(){
075 that.input.inputValue = this.firstChild.nodeValue;
076 that.popup.innerHTML='';
077 that.popup.style.display='none';
078 that.input.focus();
079 };
080 var aFocus = function(){
081 for(var i=len; i>=0; i--){
082 if(this.parentNode===that.popup.children[i]){
083 current = i;
084 break;
085 }
086 }
087 //that.input.value = this.firstChild.nodeValue;
088 for(var k in that.o.elemCSS.focus){
089 this.style[k] = that.o.elemCSS.focus[k];
090 }
091 };
092 var aBlur= function(){
093 for(var k in that.o.elemCSS.blur)
094 this.style[k] = that.o.elemCSS.blur[k];
095 };
096 var aKeydown = function(event){
097 event = event || window.event;/* 兼容IE */
098 if(current === len && event.keyCode===9){/* tab键时popup隐藏 */
099 that.popup.style.display = 'none';
100 }else if(event.keyCode==40){/* 处理上下方向键事件方便选择提示的选项 */
101 current++;
102 if(current<-1) current=len;
103 if(current>len){
104 current=-1;
105 that.input.focus();
106 }else{
107 that.popup.getElementsByTagName('a')[current].focus();
108 }
109 }else if(event.keyCode==38){
110 current--;
111 if(current==-1){
112 that.input.focus();
113 }else if(current<-1){
114 current = len;
115 that.popup.getElementsByTagName('a')[current].focus();
116 }else{
117 that.popup.getElementsByTagName('a')[current].focus();
118 }
119 }
120 };
121 for(var i=0; i<els.length; i++){/* 为每个选项添加事件 */
122 els[i].onclick = aClick;
123 els[i].onfocus = aFocus;
124 els[i].onblur = aBlur;
125 els[i].onkeydown = aKeydown;
126 }
127 };
128 this.input.onkeydown = function(event){
129 event = event || window.event;/* 兼容IE */
130 var els = that.popup.getElementsByTagName('a');
131 if(event.keyCode==40){
132 if(els[0]) els[0].focus();
133 }else if(event.keyCode==38){
134 if(els[els.length-1]) els[els.length-1].focus();
135 }else if(event.keyCode==9){
136 if(event.shiftKey==true) that.popup.style.display = 'none';
137 }
138 };
139 this.e.onmouseover = function(){ that.show=1; };
140 this.e.onmouseout = function(){ that.show=0; };
141 addEvent.call(document,'click',function(){
142 if(that.show==0){
143 that.popup.style.display='none';
144 }
145 });/* 处理提示框dom元素不支持onblur的情况 */
146 }
147 };
148 handler.prototype.init.prototype=handler.prototype;/* JQuery style,这样我们在处的时候就不用每个dom元素都用new来创建对象了 */
149 return handler;/* 把内部的处理函数传到外部 */
150 })();
151 if(this.length){/* 处理选择多个dom元素 */
152 for(var a=this.length-1; a>=0; a--){/* 调用方法为每个选择的dom生成一个处理对象,使它们不互相影响 */
153 handler(this[a],o);
154 }
155 }else{/* 处理选择一个dom元素 */
156 handler(this,o);
157 }
158 return this;
159 };
160 return window.autoComplete = autoComplete;/* 暴露方法给全局对象 */
161 /* 插件结束 */
162 })(window);

其中了一些全局的自定义函数,如addEvent和在例子中将要用到的getElementsByClassName函数如下:

01 var getElementsByClassName = function (searchClass, node, tag) {/* 兼容各浏览器的选择class的方法;(写法参考了博客园:http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1529640.html,想了解更多请看这个地址) */
02 node = node || document, tag = tag ? tag.toUpperCase() : "*";
03 if(document.getElementsByClassName){/* 支持getElementsByClassName的浏览器 */
04 var temp = node.getElementsByClassName(searchClass);
05 if(tag=="*"){
06 return temp;
07 } else {
08 var ret = new Array();
09 for(var i=0; i<temp.length; i++)
10 if(temp[i].nodeName==tag)
11 ret.push(temp[i]);
12 return ret;
13 }
14 }else{/* 不支持getElementsByClassName的浏览器 */
15 var classes = searchClass.split(" "),
16 elements = (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag),
17 patterns = [], returnElements = [], current, match;
18 var i = classes.length;
19 while(--i >= 0)
20 patterns.push(new RegExp("(^|\\s)" + classes[i] + "(\\s|$)"));
21 var j = elements.length;
22 while(--j >= 0){
23 current = elements[j], match = false;
24 for(var k=0, kl=patterns.length; k<kl; k++){
25 match = patterns[k].test(current.className);
26 if(!match) break;
27 }
28 if(match) returnElements.push(current);
29 }
30 return returnElements;
31 }
32 };
33 var addEvent=(function(){/* 用此函数添加事件防止事件覆盖 */
34 if(document.addEventListener){
35 return function(type, fn){ this.addEventListener(type, fn, false); };
36 }else if(document.attachEvent){
37 return function(type,fn){
38 this.attachEvent('on'+type, function () {
39 return fn.call(this, window.event);/* 兼容IE */
40 });
41 };
42 }
43 })();

最后是调用的部分,调用和每个参数的部分都有说明和注意事项,再说一个其中source和ajax参数是二选一,如果二者都写只有source是有用的,调用代码如下:

1 addEvent.call(null,'load',function(){
2 autoComplete.call( getElementsByClassName('autoComplete'), {/* 使用call或apply调用此方法 */
3 source:['0123','023',123,1234,212,214,'033333','0352342',1987,17563,20932],/* 提示时在此数组中搜索 */
4 //ajax:{ type:'post',url:'./php/fetch.php?search=' },/* 如果使用ajax则返回的数据格式要与source相同,如为字符串"[111,222,333,444]"等形式。*/
5 elemCSS:{ focus:{'color':'#00ff00','background':'red'}, blur:{'color':'#ff0000','background':'transparent'} },/* 些对象中的key要js对象中的style属性支持 */
6 input:'input',/* 输入框使用input元素 */
7 popup:'ul'/* 提示框使用ul元素 */
8 });
9 });

代码比较多,但是测试的比较完整,基本没有什么大毛病了,如果有什么毛病请大家指正,谢谢!

下面是个完整的例子,大家可以在线测试一下,欢迎指正:

posted @ 2011-09-01 13:27  谢多  阅读(307)  评论(0)    收藏  举报