目前大多数搜索框都已实现自动补全功能,自己也私底下实现了一个简易版本,
在此总结过程中的一些要点:
1,侦听文本框的value值改变,注意在Ie8及其之前版本的onpropertychange和Ie9的oninput事件与
W3C下的oninput事件的异同;
2,ajax请求数据;
3,自动补全框的定位;
4,上下键导航以及鼠标导航
在此附上源码:
.auto-ul{ list-style: none; padding: 0; margin: 0; } .auto-ul li{ margin: 0; padding:5px; } .auto-ul a{ text-decoration: none; color: black; } .w{ background: #cecece; cursor: default; } input{ border: 1px solid #c0c0c0; }
<div class="w250 h200 bgf0 auto tc pt15"> <div> <form> <label for="t">username: </label> <input type="text" id="t" autocomplete="false" class="p5 w150 lh22"> </form> </div> </div>
items.txt文件
<ul>
<li>
<a href="#" class="db">java</a>
</li>
<li>
<a href="#" class="db">javaWeb</a>
</li>
<li>
<a href="#" class="db">javaScript</a>
</li>
<li>
<a href="#" class="db">javaScript & CSS</a>
</li>
</ul>
function $(m){ return document.getElementById(m) } function createBox(){ var div = document.createElement(\'div\'),w; w = $(\'t\').offsetWidth; div.id = \'box\'; div.style.cssText = \'position:absolute;width:\'+(w-2)+\'px;border:1px solid #cecece;display:none;\'; div.innerHTML = \'<ul class="auto-ul" id="autobox-ul"></ul>\' return div; } function showBox(d,boxLocation){ d.style.display = \'\'; d.style.left = boxLocation.left + \'px\'; d.style.top = boxLocation.top + \'px\'; } function hideBox(d){ d.style.display = \'none\'; d.getElementsByTagName(\'ul\')[0].innerHTML = \'\'; } //创建xhr function createXHR(){ if(\'XMLHttpRequest\' in window){ createXHR = function(){ return new XMLHttpRequest(); } }else{ var i= 0,len, fns = [function(){return new ActiveXObject(\'Microsoft.XMLHTTP\')},function(){return new ActiveXObject(\'Msxml2.XMLHTTP\')}, function(){return new ActiveXObject(\'Msxml2.XMLHTTP.3.0\')},function(){return new ActiveXObject(\'Msxml2.XMLHTTP.6.0\')}]; for(len = fns.length;i<len;i++){ try{ fns[i](); createXHR = fns[i]; break; }catch (e){ } } } return createXHR(); } // ajax实现 function ajaxInit(ajaxData){ var xhr = createXHR(),get_data,isLoaded = false, map = { \'html\': \'text\', \'arraybuffer\': \'arraybuffer\', \'blob\': \'blob\', \'document\': \'document\', \'json\': \'text\' }; ajaxData.onBefore = ajaxData.onBefore || function(){}; ajaxData.onSuccess = ajaxData.onSuccess || function(){}; ajaxData.onFailure = ajaxData.onFailure || function(){}; ajaxData.onComplete = ajaxData.onComplete || function(){}; ajaxData.timeout = ajaxData.timeout || 5000; ajaxData.type = ajaxData.type || \'post\'; /** * \'text\':返回类型为字符串,这是默认值。 \'arraybuffer\':返回类型为ArrayBuffer。 \'blob\':返回类型为Blob。 \'document\':返回类型为Document,用于xml。 \'json\':返回类型为JSON object,支持JSON的浏览器(Firefox>9,chrome>30), 就会自动对返回数据调用JSON.parse() 方法。也就是说,你从xhr.response属性 (注意,不是xhr.responseText属性)得到的不是文本,而是一个JSON对象。 */ if(xhr.responseType) xhr.responseType = ajaxData.responseType in map ? map[ajaxData.responseType] : \'text\'; ajaxData.onBefore(); xhr.open(ajaxData.type,ajaxData.url,true); xhr.onreadystatechange = function(){ if(xhr.readyState == 4 && !isLoaded){ // 判断响应成功的几点: // 1,如果是访问本地文件,请求成功但不会获得响应码 // 2,IE(通过ActiveXObject创建的xhr对象)会将204设置为1223 // 3, opera会将204设置为0 if(!xhr.status && location.protocol == \'file:\' || xhr.status == 1223 || xhr.status == 0 || xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){ if(ajaxData.responseType.toLowerCase() == \'json\'){ get_data = \'JSON\' in window ? JSON.parse(xhr.responseText) : new Function(\'return \' + xhr.responseText + ";"); }else if(ajaxData.responseType.toLowerCase() == \'html\'){ get_data = xhr.responseText; }else if(xhr.responseType.toLowerCase() == \'document\'){ get_data = xhr.responseXML; }else{ get_data = xhr.response; } ajaxData.onSuccess(get_data); }else{ ajaxData.onFailure(); } ajaxData.onComplete(); xhr = null; } } setTimeout(function(){ isLoaded = true; },ajaxData.timeout); if(ajaxData.type.toLowerCase() == \'get\'){ xhr.setRequestHeader(\'X-Request-With\',\'XMLHttpRequest\'); xhr.send(null); }else if(ajaxData.type.toLowerCase() == \'post\' && ajaxData.data){ xhr.setRequestHeader(\'X-Request-With\',\'XMLHttpRequest\'); xhr.setRequestHeader(\'content-type\',\'application/x-www-form-urlencoded\'); xhr.send(ajaxData.data); } return xhr; } function addEvent(el,type,fn){ if(window.addEventListener){ el.addEventListener(type,fn,false) }else{ el.attachEvent(\'on\'+type,function(){ var e = window.event; e.preventDefault = function(){ e.returnValue = false; }; e.stopPropagation = function(){ e.cancelBubble = true; } fn.call(el,e); }) } } //给文本框绑定事件 function bindEvent(t,fn){ var input = t; //对输入框绑定事件 if(input.addEventListener){ input.addEventListener(\'input\',fn,false); }else{ input.attachEvent(\'onpropertychange\',function(){ var e = window.event; if(e.propertyName == \'value\'){ fn(); } }); } if(window.VBArray && window.addEventListener && !window.WebSocket){ input.addEventListener(\'keyup\',function(e){ var code = e.keycode || e.charcode; if(code==8 || code==46){ fn(); } },false) ; input.oncut=function(){fn()}; } //对按键事件侦听 addEvent(input,\'keydown\',function(e){ var l,ol = -1,nl; if(e.keyCode == 40 || e.keyCode == 38){ e.preventDefault(); l = $(\'autobox-ul\').getElementsByTagName(\'li\'); for(var i=0,len=l.length;i<len;i++){ if(l[i].className == \'w\'){ ol = i; //保存当前选定的选项 } l[i].className = \'\'; } if(e.keyCode == 40 || e.charCode == 40){ //下箭头 ol++; if(ol <= len-1){ l[ol].className = \'w\'; nl = l[ol]; }else{ l[0].className = \'w\'; nl = l[0]; } }else if(e.keyCode == 38 || e.charCode ==38){ //上箭头 ol--; if(ol >= 0){ l[ol].className = \'w\'; nl = l[ol]; }else{ l[l.length-1].className = \'w\'; nl = l[l.length-1]; } } this.value = nl.getElementsByTagName(\'a\')[0].innerHTML; nl.className = \'w\'; } }); addEvent($(\'box\'),\'mousemove\',function(e){ var t = e.target || e.srcElement, l = $(\'autobox-ul\').getElementsByTagName(\'li\'); e.preventDefault(); for(var i= 0,len=l.length;i<len;i++){ l[i].className = \'\'; } if(t.tagName.toLowerCase() == \'a\'){ t.parentNode.className = \'w\'; input.value = t.innerHTML; } }) //若输入框失去焦点,则隐藏补全框 addEvent(input,\'blur\',function(){ hideBox($(\'box\')) }) } (function(){ var t = $(\'t\'),div,boxLocation,ul; div = createBox(); boxLocation = { left: t.getBoundingClientRect().left + parseInt(document.documentElement.scrollLeft || document.body.scrollLeft || 0) - parseInt(document.documentElement.clientLeft || document.body.clientLeft || 0), top: t.getBoundingClientRect().top + parseInt(document.documentElement.scrollTop || document.body.scrollTop || 0) - parseInt(document.documentElement.clientTop || document.body.clientTop || 0) + parseInt(t.offsetHeight) }; document.body.appendChild(div); ul = $(\'autobox-ul\'); bindEvent(t,function(){ var value = t.value; ajaxInit({ type: \'get\', timeout: 3000, url: \'./items.txt\', responseType: \'html\', onSuccess: function(data){ var d = document.createElement(\'div\'); d.innerHTML = data; d = d.getElementsByTagName(\'ul\')[0]; ul.innerHTML = d.innerHTML; showBox(div,boxLocation) } }) }) })()
经测试,IE8及其之前版本有bug,主要是因为onpropertychange的原因导致无法直接给文本框赋值。待修改。