⽤Html+Js实现的“⾃动补全”功能
正⽂:
这⼏天,帮同学解决⼀个问题,虽然他的问题还没有完全解决,但在解决问题的过程中我做了这个“⾃动补全”的功能。虽然这个补全的功能在⽹上也有很多代码,但是在我写代码和学习的过程中,好的较少,清晰的较少,多是转载或是浮躁的写⼀写,我觉得这样很容易使有问题的⼈产⽣⼊门易,理解难的问题。所以我下⾯会把⾃⼰学习和写这个的过程尽量写的详细⼀些。
需求:
这个其实你登录到百度⾸页,我的需求主要是参考这个搜索框的“⾃动补全”功能,⾃⼰随便输⼊个⽂字,⽐如:“我”,看看结果理解下吧~
实现:
1.可以根据⽂本框的“输⼊”⾃动完成与“输⼊”相关的数据的匹配和前台展⽰。
2.可以通过键盘⽅向键实现浏览。
3.可以通过⿏标的点击和键盘的回车选中需要的数据项并更新显⽰在⽂本框中。
实现备注:
1.在实现的过程中,后台数据的模拟,是通过数组在js部分暂时实现,真正⽤到项⽬上我们可以通过ajax来实现,这是后话,暂不详说。
2.⾄于兼容性来说,我测试过ie8、chrome、firefox、遨游、360等浏览器。其它的浏览器,尤其是ie系列,如果⼤家愿意帮这个忙,可以帮忙测试⼀下。因为我后边还想把这个封装成jsp的⾃定义标签,所以这个还是很需要⼤家的帮助的,先谢谢⼤家了~~综上,废话不多说,现在开始正是来分析下如何来实现这个功能,开整:
==========================================
分析html的结构:
1.这个通过我⾃⼰想,我想应该包括⼀个⽂本框,那下⾯⽤来显⽰搜索数据的列表应该⽤什么呢,我想过select、ul和table,后来还是打开百度⾸页,输⼊了个“我”,再F12看了下它的结构,我决定就⽤他那个吧,详细如下:
2.我的具体实现如下:
<body onload="initialize()">
<div id="bodyDiv" name="bodyDiv" style="position: relative; border-top:0px; margin-top:0; width:160p
x">
<!--<span >*补全功能默认开启</span>-->
<input type="text" id="inputUser" name="inputUser" value="" onfocus="inputFocus()" onkeydown="inputKeydown(event)" onblur="inputBlur()"/>
<div id="showUser" name="showUser">
<!-- 此处添加搜索数据的结果,使⽤的是table标签 -->
</div>
</div>
</body>
⼤家可以看到我的html结构,我打算⽤table来做具体的列表组件,这⾥提⼀句后话:我的table的id为selector,在js中通过
getElementById获得的该table并赋值给⼀个select变量。⼤家在此先不⽤我这么做的意义,只要先知
道有这么回事⼉就好。
⼤家应该还可以看到,我在html中绑定了⼏个事件:
a.initialize(),这个函数主要⽤于在页⾯加载完毕后⽴即初始化⼀些变量。
b.inputFocus(),这个函数主要是当焦点移动到⽂本框上时调⽤
c.inputKeydown(),这个函数主要是当焦点在⽂本框上时,⽤户触发了键盘事件,调⽤此函数处理
d.inputBlur(),这个事件主要是当焦点离开了⽂本框时,触发该事件。
开始分析js部分:
1.initialize()函数:
我们⾸先要明⽩⼀件事情,当页⾯加载完毕后,我们需要看到什么?⼀个⽂本框!我们不需要看到什么?那个搜索数据的列表!顺便提⼀句,什么时候需要看到那个列表呢?那就是当⽂本框中输⼊了数据(此处我定义的数据都是数字,所以输⼊的时候若想有结果也暂时输⼊数字),如果有匹配该输⼊的数据,则要实时的更新并显⽰列表。
我们的思路暂时理到这⾥,其它的留待后话再说,先看代码:
//加载完后,将"提⽰"列表隐藏
function initialize(){
source=['0123','023',123,1234,212,214,'033333','0352342',1987,17563,20932];
elemCSS={ focus:{'color':'black','background':'#ccc'}, blur:{'color':'black','background':'transparent'} };
ElementById("inputUser");
ElementById("showUser");
var ElementById("bodyDiv");
showUse.style.display="none";
inputUse.style.width=bodyDiv.style.width;
showUse.style.width=bodyDiv.style.width;
inputValue=inputUse.defaultValue;
}
这⾥着重要说的是函数中后四⾏的代码。对于涉及“width”的部分,⾸先我们先设置⽤来包含table的div为隐藏状态,这样,table也将是隐藏的。同时将⽂本框的宽度和列表的宽度也进⾏设置,此处我⽤到的是最外层div的宽度。对于最后⼀⾏的代码,我们取出了⽂本框的默认值(空值)赋值给⼀个全局变量,这个变量之后的主要作⽤就是⽤来记录⽂本框之前的值,以便⽤这个值与⽂本框的当前值相⽐较,如果值改变了,则需要去后台查询匹配的数据项。
2.inputFocus()函数:
⼤家想想初始化完之后,我们眼前出现的应该是⼀个⽂本框,⽽“提⽰”列表此时应该是隐藏的。当我们把⿏标在⽂本框点击⼀下,页⾯的焦点就会切换到⽂本框上,此时也就出发⽂本框的onfocus事件,调⽤inputFocus()函数,代码如下:
//在⽂本框上触发onfocus()事件
function inputFocus(){
//调⽤setInterval()函数每200ms刷新⼀次
this.timer=setInterval(function(){
if(inputUse.value!=''){
//检查⽂本框的当前值与以前的值是否有变化
if(inputUse.value!=inputValue){
//如果⽂本框当前值与之前的值不同,记录当前值,已被下次调⽤时使⽤
inputValue=inputUse.value;
//清除上次调⽤显⽰的结果
showUse.innerHTML='';
if(inputValue!=''){
//定义JS的RegExp对象,查询以inputValue开头的数据
quickExpr=RegExp('^'+inputValue);
/
/如果数据源不为空,则调⽤match函数开始匹配数据
//此处如果通过ajax取数据,则适当修改数据源即可
if(source){
match(quickExpr,inputValue,source);
}
}
}
}
else{
inputValue=inputUse.value;
showUse.innerHTML='';
showUse.style.display="none";
}
},200)
}
从代码,我们可以看出,我们的主体内容定义在了setInterval()这个函数中。为什么要这样定义呢?因为⼀旦焦点在⽂本框中,我们就需要时刻知道⽂本框的内容是否发⽣了变化,以便匹配数据,所以我们利⽤setInterval函数每200m对⽂本框执⾏⼀次判断。
那么该如何判断呢,之前提到过inputValue,主要是利⽤这个值与⽂本框的当前值作⽐较(从代码中你可以体会到我的意思),并且要更新inputValue的值,以备下次调⽤使⽤。如果,⽂本框的值与200ms之前⽐发⽣了变化,我们就要利⽤js的RegExp对象来访问match()这个函数,这个函数的代码如下:
//该函数⽤来查询匹配数据
function match(quickExpr,value,source){
var table=null;
var tr=null;
var td=null;
//创建table标签
ateElement('table');
table.id='selector';
table.style.width='100%';
//开始遍历数组
//如果⽤ajax从后台取数据,我们也可组织成数组的形式返回
for(var i in source){
//再次检验数据是否为空并且⽤正则取数据
if(value.length>0 && (source[i])!=null){
/
/创建tr标签
ateElement('tr');
//创建td标签
ateElement('td');
//在td中插⼊<a href="javascript:void(null);"><span>数据项</span></a>
td.innerHTML = '<a href="javascript:void(null);">'+source[i]+'</a>';
//appendChild()在指定元素的最后⼀个⼦节点后添加节点
tr.appendChild(td);
table.appendChild(tr);
showUse.appendChild(table);
}
}
//检验table下⾯的a标签的数量,以此确定是否将“提⽰”列表显⽰
ElementsByTagName('a').length){
showUse.style.display="";
}else{
showUse.style.display="none";
}
}
针对match函数的代码,我在代码中添加的注释相信已经对你的理解有很⼤的帮助,但我还要在强调的是我选择在此创建包含“提⽰”列表的table,并根据查询到的符合条件的数据量创建对应的tr,td。这个最终将形成的结构你可以参考上⾯我对于百度⾸页的搜索框的截图,以便更好的理解。
3.inputKeydown()函数:
当我们通过上述两步,我们页⾯上的“提⽰”列表已经显⽰。我们必然会通过⽅向键等键盘按钮访问列表中的数据。这⼀个过程中,主要的变化是焦点由⽂本框内变化到了“提⽰”列表中,代码如下:
function inputKeydown(event){
//兼容IE
event = event || window.event;
//如果按了down键
if(event.keyCode==40){
//如果“提⽰”列表已经显⽰,则把焦点切换到列表中的第⼀个数据项上
if(showUse.style.display==""){
}else{  //如果“提⽰”列表未显⽰,则把焦点依旧留在⽂本框中
inputUse.focus();
}
input绑定onblur事件}
//如果按了up键
else if(event.keyCode==38){
//如果“提⽰”列表已经显⽰,则把焦点切换到列表中的最后⼀个数据项上
if(showUse.style.display==""){
}else{    //如果“提⽰”列表未显⽰,则把焦点依旧留在⽂本框中
inputUse.focus();
}
}
/
/如果按了tab键,此时的情况与“百度⾸页”的处理情况⼀样
else if(event.keyCode==9){
showUse.innerHTML='';
showUse.style.display="none";
}
}
这⾥主要想说的是,当"提⽰"列表已经展开的情况下,如果按了up或者down键(根据事件对象的键码值判断),则将数据项的最后⼀项或者第⼀项设置为⾼亮,并且将焦点真正切换到相应的⾼亮数据项上。
但是这⾥还需要着重注意的是按了tab键该如何处理。这⾥的处理⽅式与百度相同,就是关闭"提⽰"列表。
4.inputBlur函数:
这个函数的定义我们要理解下,这个事件定义的是⽂本框的onblur事件,该事件的含义是当焦点离开
元素对象(此处就是指⽂本框)时调⽤。那么具体的内容,我们先来看看代码:
//当焦点离开⽂本框时,触发该事件
function inputBlur(){
//由于焦点已经离开了⽂本框,则取消setInterval
clearInterval(this.timer);
//记住当前有焦点的选项
var current=0;
//当前table下⾯的a标签的个数
var ElementsByTagName('a');
var len=aArray.length-1;
var ElementById("selector");
/
/定义“选项”的onclick事件
var aClick = function(){
//由于“选项”上触发了click事件,this就是指a标签,则把a标签包含的数据赋值给⽂本框                inputUse.value=this.childNodes[0].data;
//将⽂本框的当前值更新到记录以前值的变量中
inputValue=inputUse.value;
//由于上⾯已经选出合适的数据项,则清空table下的内容,并关闭“提⽰”列表
showUse.innerHTML='';
showUse.style.display='none';
//将焦点移回⽂本框
inputUse.focus();
};
//定义“选项”的onfocus事件
var aFocus = function(){
for(var i=len; i>=0; i--){
//this是a,this.parentNode是td,select.children[i].children[0]是d
if(this.parentNode===select.childNodes[i].childNodes[0]){
//如果是同⼀个td,则将current的值置为焦点所在位置的值
current = i;
break;
}
}
//添加有焦点的效果
for(var k in elemCSS.focus){
this.style[k] = elemCSS.focus[k];
}
};
//定义“选项”的onblur事件
var aBlur= function(){
//添加⽆焦点的效果
for(var k in elemCSS.blur)
this.style[k] = elemCSS.blur[k];
};
//定义“选项”的onKeydown是事件
var aKeydown = function(event){
//兼容IE
event = event || window.event;
//如果在选择数据项时按了tab键,此时的情况与“百度⾸页”的处理情况⼀样
if(event.keyCode===9){
showUse.innerHTML='';
showUse.style.display = 'none';
inputUse.focus();
}
//如果按了down键
else if(event.keyCode==40){
/
/向下移动,准备移动焦点
current++;
//如果当前焦点在最后⼀个数据项上,⽤户⽤按了down键,则循环向上,回到⽂本框上
if(current>len){
current=-1;
inputUse.focus();
}else{
}
}
//如果按了up键
else if(event.keyCode==38){
//向上移动,准备移动焦点
current--;
//如果当前焦点在⽂本框上,⽤户⽤按了up键,则循环向下,回到最后⼀个数据项上
if(current<0){
inputUse.focus();
}else{
}
}
};
/
/将“选项”的事件与相应的处理函数绑定
for(var i=0; i<aArray.length; i++){
aArray[i].onclick = aClick;
aArray[i].onfocus = aFocus;
aArray[i].onblur = aBlur;
aArray[i].onkeydown = aKeydown;
}
}
上⾯代码的内容很长,但是⼤家要⾸先抓住主要的来看。我的建议是先看最后那个for循环。我在这⾥把定义的四个事件绑定到了⼀个数组的对象上,⽽这个数组中的对象就是"提⽰"列表中每⼀个数据项。到此,我想⼤家应该应该还记得上⾯的inputKeydown()函数了吧,通过该函数将焦点由⽂本框切换到“提⽰”列表上,更确切的说是数据项上,此处对应的是<a>标签。那么在数据项上的移动以及选中
就要依靠上⾯定义的四个事件。四个事件的具体分析请⼤家详细的看注释,有些不懂的地⽅不妨⽤浏览器的调试来看看就明⽩了。
⾃⼰的收获和感想:
我先说说技术上的吧,我最初利⽤table调⽤innerHTML并将其值设置为空,来实现"提⽰"列表的清除,使下次调⽤时仅仅显⽰当次调⽤的正确结果。我最初代码的测试是⾯向chrome的,但后来再调试兼容性时,发现在ie下,table是⽆法对innerTHML属性进⾏设置值的,但是可读。再纠结了⼀下之后,发现其实兼容性的主要问题还就是集中在这个上⾯,于是果断重新写,最终处理了这个问题。
还有⼀个就是z-index的使⽤,这⾥不详述,但感兴趣的同学可以翻看《程序员》杂志2013.2⽉版的⽂章:不为⼈知的z-index,写的相当不错。
其实最⼤的体会就是,⾃⼰动⼿写。虽然⽹上也有很多⼈写过,但我保证很多是浅尝辄⽌的。就像我曾经在⼀个⾥问了个关于焦点切换的问题,很多⼈都说这个东西都写烂了,当时给我的感觉就是我这个问题问的很⼩⽩阿。但我坚持说向⾃⼰写写,后来其中的⼀个说简单的那位看了代码也说不会。这就说明⼀个问题,很多事情别⼈轻视了,⽤插件来解决了,如果你在有精⼒的情况下,最好还是⾃⼰动⼿写写。
我觉的⽐较好的⽅法就是,结合我这篇博客来说,我觉得⼤家可以先点开百度⾸页⾃⼰理清思路,能理到哪⼉算哪⼉。然后再看看我这篇博客,加深对实现的了解。之后最好就是⾃⼰先写写了,其实按照这个的难度,⼀般⼈都可以写到焦点的切换。最后,如果实在没想法的就看看我附件只中的源码吧。加上注释,我相信⼀定可以使你较为轻松的明⽩问题。
最后,希望⼤家共同进步~~
PS:我怎么没到添加附件的地⽅。。。。,还有怎么在我编辑这个⽂章时,⾏距怎么⼀会⼉⼤⼀会⼉⼩啊。。。。望⾼⼿指点

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。