以前也写过一个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 |