用js实现输入提示(自动完成)
以前也写过一个jQuery的这种插件,但是很多地方根本不用jQuery,这个功能也有很多其它库支持,但是为了用这个功能而加载很多js插件,这样效率明显下降了很多,而且这个东西平时也很常用,所以一决心自己写了个相对比较独立的。
完成有以下功能:
- 输入字符会把以输入字符开头的提示出来。
- 支持上下方向键选择提示选项,支持循环
- 支持绑定一个数组提示,支持ajax传递输入框值请求数据。
- 支持多个选择的dom元素一块绑定数据实现输入提示。各dom元素也可以单独绑定自己的数据实现输入提示,互不影响。
- 支持中文。
首先是js的核心部分,其各部分都有较详细的说明,代码如下:
1 | ;( function (window){<br> /* 插件开始 */ <br> var autoComplete= function (o){<br> var handler=( function (){<br> var handler= function (e,o){ return new handler.prototype.init(e,o); }; /* 为每个选择的dom都创建一个相对应的对象,这样选择多个dom时可以很方便地使用 */ <br> handler.prototype={<br> e: null , o: null , timer: null , show:0, input: null , popup: null ,<br> init: function (e,o){ /* 设置初始对象 */ <br> this .e=e, this .o=o,<br> this .input= this .e.getElementsByTagName( this .o.input)[0],<br> this .popup= this .e.getElementsByTagName( this .o.popup)[0],<br> this .initEvent(); /* 初始化各种事件 */ <br> },<br> match: function (quickExpr,value,source){ /* 生成提示 */ <br> var li = null ;<br> for ( var i in source){<br> if ( value.length>0 && quickExpr.exec(source[i])!= null ){<br> li = document.createElement( 'li' );<br> li.innerHTML = '<a href="javascript:;">' +source[i]+ '</a>' ;<br> this .popup.appendChild(li);<br> }<br> }<br> if ( this .popup.getElementsByTagName( 'a' ).length)<br> this .popup.style.display= 'block' ;<br> else <br> this .popup.style.display= 'none' ;<br> },<br> ajax: function (type,url,quickExpr,search){ /* ajax请求远程数据 */ <br> var xhr = window.ActiveXObject ? new ActiveXObject( "Microsoft.XMLHTTP" ) : new XMLHttpRequest();<br> xhr.open(type,url, true ); /* 同异步在此修改 */ <br> xhr.setRequestHeader( "Content-Type" , "application/x-www-form-urlencoded" );<br> var that= this ;<br> xhr.onreadystatechange = function (){<br> if (xhr.readyState==4) {<br> if (xhr.status==200) {<br> var data = eval(xhr.responseText);<br> that.match(quickExpr,search,data); /* 相同于成功的回调函数 */ <br> } else {<br> alert( "请求页面异常!" ); /* 请求失败 */ <br> }<br> }<br> };<br> xhr.send( null );<br> },<br> fetch: function (ajax,search,quickExpr){<br> var that= this ;<br> this .ajax(ajax.type,ajax.url+search,quickExpr,search);<br> },<br> initEvent: function (){ /* 各事件的集合 */ <br> var that= this ;<br> this .input.onfocus = function (){<br> var value= this .value, quickExpr=RegExp( '^' +value, 'i' ), self= this ;<br> that.timer=setInterval( function (){<br> if (value!=self.value){ /* 判断输入内容是否改变,兼容中文 */ <br> value=self.value;<br> that.popup.innerHTML= '' ;<br> if (value!= '' ){<br> quickExpr=RegExp( '^' +value);<br> if (that.o.source) that.match(quickExpr,value,that.o.source);<br> else if (that.o.ajax) that.fetch(that.o.ajax,value,quickExpr);<br> }<br> }<br> },200);<br> };<br> this .input.onblur = function (){ /* 输入框添加事件 */ <br> clearInterval(that.timer);<br> var current=-1; /* 记住当前有焦点的选项 */ <br> var els = that.popup.getElementsByTagName( 'a' );<br> var len = els.length-1;<br> var aClick = function (){<br> that.input.value= this .firstChild.nodeValue;<br> that.popup.innerHTML= '' ;<br> that.popup.style.display= 'none' ;<br> };<br> var aFocus = function (){<br> for ( var i=len; i>=0; i--){<br> if ( this .parentNode===that.popup.children[i]){<br> current = i;<br> break ;<br> }<br> }<br> for ( var k in that.o.elemCSS.focus)<br> this .style[k] = that.o.elemCSS.focus[k];<br> };<br> var aBlur= function (){<br> for ( var k in that.o.elemCSS.blur)<br> this .style[k] = that.o.elemCSS.blur[k];<br> };<br> var aKeydown = function (event){<br> event = event || window.event; /* 兼容IE */ <br> if (event.keyCode==40){ /* 处理上下方向键事件方便选择提示的选项 */ <br> current++;<br> if (current<0) current=len; /* 处理循环 */ <br> if (current>len) current=0;<br> that.popup.getElementsByTagName( 'a' )[current].focus();<br> } else if (event.keyCode==38){<br> current--;<br> if (current>len) current=0;<br> if (current<0) current=len;<br> that.popup.getElementsByTagName( 'a' )[current].focus();<br> }<br> };<br> for ( var i=0; i<els.length; i++){ /* 为每个选项添加事件 */ <br> els[i].onclick = aClick;<br> els[i].onfocus = aFocus;<br> els[i].onblur = aBlur;<br> els[i].onkeydown = aKeydown;<br> }<br> };<br> this .input.onkeydown = function (event){<br> event = event || window.event; /* 兼容IE */ <br> if (event.keyCode==40){<br> that.popup.blur();<br> if (that.popup.getElementsByTagName( 'a' )[0])<br> that.popup.getElementsByTagName( 'a' )[0].focus();<br> }<br> };<br> this .e.onmouseover = function (){ that.show=1; };<br> this .e.onmouseout = function (){ that.show=0; };<br> addEvent.call(document, 'click' , function (){ if (that.show==0) that.popup.style.display= 'none' ; }); /* 处理提示框dom元素不支持onblur的情况 */ <br> }<br> };<br> handler.prototype.init.prototype=handler.prototype; /* JQuery style,这样我们在处的时候就不用每个dom元素都用new来创建对象了 */ <br> return handler; /* 把内部的处理函数传到外部 */ <br> })();<br> if ( this .length){ /* 处理选择多个dom元素 */ <br> for ( var a= this .length-1; a>=0; a--){ /* 调用方法为每个选择的dom生成一个处理对象,使它们不互相影响 */ <br> handler( this [a],o);<br> }<br> } else { /* 处理选择一个dom元素 */ <br> handler( this ,o);<br> }<br> return this ;<br>};<br> return window.autoComplete = autoComplete; /* 暴露方法给全局对象 */ <br> /* 插件结束 */ <br>})(window);<br> |
其中了一些全局的自定义函数,如addEvent和在例子中将要用到的getElementsByClassName函数如下:
1 | var getElementsByClassName = function (searchClass, node, tag) { /* 兼容各浏览器的选择class的方法;(写法参考了博客园:http://www.cnblogs.com/rubylouvre/archive/2009/07/24/1529640.html,想了解更多请看这个地址) */ <br> node = node || document, tag = tag ? tag.toUpperCase() : "*" ;<br> if (document.getElementsByClassName){ /* 支持getElementsByClassName的浏览器 */ <br> var temp = node.getElementsByClassName(searchClass);<br> if (tag== "*" ){<br> return temp;<br> } else {<br> var ret = new Array();<br> for ( var i=0; i<temp.length; i++)<br> if (temp[i].nodeName==tag)<br> ret.push(temp[i]);<br> return ret;<br> }<br> } else { /* 不支持getElementsByClassName的浏览器 */ <br> var classes = searchClass.split( " " ),<br> elements = (tag === "*" && node.all)? node.all : node.getElementsByTagName(tag),<br> patterns = [], returnElements = [], current, match;<br> var i = classes.length;<br> while (--i >= 0)<br> patterns.push( new RegExp( "(^|\\s)" + classes[i] + "(\\s|$)" ));<br> var j = elements.length;<br> while (--j >= 0){<br> current = elements[j], match = false ;<br> for ( var k=0, kl=patterns.length; k<kl; k++){<br> match = patterns[k].test(current.className);<br> if (!match) break ;<br> }<br> if (match) returnElements.push(current);<br> }<br> return returnElements;<br> }<br>};<br> var addEvent=( function (){ /* 用此函数添加事件防止事件覆盖 */ <br> if (document.addEventListener){<br> return function (type, fn){ this .addEventListener(type, fn, false ); };<br> } else if (document.attachEvent){<br> return function (type,fn){<br> this .attachEvent( 'on' +type, function () {<br> return fn.call( this , window.event); /* 兼容IE */ <br> });<br> };<br> }<br>})();<br> |
最后是调用的部分,调用和每个参数的部分都有说明和注意事项,再说一个其中source和ajax参数是二选一,如果二者都写只有source是有用的,调用代码如下:
1 | addEvent.call( null , 'load' , function (){<br> autoComplete.call( getElementsByClassName( 'autoComplete' ), { /* 使用call或apply调用此方法 */ <br> source:[ '0123' , '023' ,123,1234,212,214, '033333' , '0352342' ,1987,17563,20932], /* 提示时在此数组中搜索 */ <br> //ajax:{ type:'post',url:'./php/fetch.php?search=' },/* 如果使用ajax则返回的数据格式要与source相同,如为字符串"[111,222,333,444]"等形式。*/<br> elemCSS:{ focus:{'color':'#00ff00','background':'red'}, blur:{'color':'#ff0000','background':'transparent'} },/* 些对象中的key要js对象中的style属性支持 */<br> input:'input',/* 输入框使用input元素 */<br> popup:'ul'/* 提示框使用ul元素 */<br> });<br>});<br> |
代码比较多,但是测试的比较完整,基本没有什么大毛病了,如果有什么毛病请大家指正,谢谢!