在web开发中,为了提高用户体验,会经常用到输入框的自动完成功能,不仅帮助用户进行快速输入,最重要的是帮助那些“记不全要输入什么”的用户进行选择。这个功能有很多插件已经实现了,为了适应项目的特殊需求,决定自己编写一个具备通用性、扩展性和灵活性的自动完成类,就当是边写边学习了,一举两得。该功能是比较简单的,核心是数据获取方式和导航的实现,简单写了一个,经测试非常好用,还有很多地方需要修改和改进,例如:在原型中只暴露init方法即可,其他方法都需要放到私有空间内,不让用户访问到,这个以后再完善吧。啥也不说了,小二,上菜:
代码如下:(已更新,最新代码请参考:https://github.com/zjh-neverstop/AutoCompleteMulti)
1 /** 2 * 实现自动完成功能的js类 3 * 1、数据获取方式:设置静态数据集、Ajax方式、自定义数据获取函数 4 * 2、可以控制是否启用匹配项的循环选择 5 * 3、可以控制是否使用默认静态数据集,在获取动态数据失败的情况下会显示 7 */ 8 9 (function(){ 10 11 //封装使用频繁的变量,在构造函数中进行初始化 12 var commonObj = {}; 13 14 /** 15 * 构造函数 16 * @param option 17 * @constructor 18 */ 19 function AutoComplete(option) { 20 //需要实现自动完成功能的页面控件ID 21 this.controlId = option.controlId; 22 23 //匹配结果div的id 24 this.resultDivId = option.resultDivId; 25 26 //当前选中的项索引,第一项索引为0 27 this.index = -1; 28 29 //静态数据集 30 this.datas = option.datas; 31 32 //动态获取的数据集 33 this.dynamicDatas = null; 34 35 //服务器端地址 36 this.serverUrl = option.serverUrl; 37 38 //匹配结果集合 39 this.resultDatas = null; 40 41 //ajax请求数据 42 this.ajaxRequestData = option.ajaxRequestData; 43 44 //主要用在一个后台页面处理多个前端自动完成请求的情况,根据此字段调用具体的后台方法 45 this.actionName = option.actionName; 46 47 //是否可以循环选择 48 this.circleChoose = option.circleChoose || "true"; 49 50 //是否从服务器端获取数据 51 this.serverEnabled = option.serverEnabled || "false"; 52 53 //是否使用静态数据 54 //一般情况下,自动完成都是获取动态数据的,开启这个标志后,在获取动态数据失败的情况下会使用静态数据,默认为false 55 this.useStaticDatas = option.useStaticDatas||"false"; 56 57 //驱动函数 58 this.drivenFuc = null; 59 60 //自定义数据获取方法 61 this.getCompleteDatas = function (){ 62 if( typeof option.getCompleteDatas === 'function' ){ 63 return option.getCompleteDatas(); 64 } 65 else{ 66 return null; 67 } 68 69 }; 70 71 72 //私有变量,封装后面常用的4个变量 73 //var commonObj = {}; 74 75 //通过匿名函数来构造一个类似面向对象语言中的只读属性 76 //初始化commonObj变量 77 //注意:匿名函数中的this指向windows对象,这里需要将AutoComplete对象的引用赋值给that 78 (function(that){ 79 commonObj = { 80 control : document.getElementById(that.controlId), 81 results : document.getElementById(that.resultDivId), 82 jControl : $(document.getElementById(that.controlId)), 83 jResults : $(document.getElementById(that.resultDivId)) 84 }; 85 })(this); 86 87 //只读属性 88 /*this.getCommonObj = function(){ 89 return commonObj; 90 }*/ 91 92 } 93 94 /** 95 * 原型方法,除了init方法和构造函数外,其他方法均需要私有化,待完善... 96 */ 97 AutoComplete.PRototype = { 98 99 //指定构造函数100 constructor: AutoComplete,101 102 //获取事件对象103 getEvent: function() {104 return window.event || arguments[0]; //event ? event : window.event;105 },106 107 //获取事件源108 getTarget: function(event) {109 return event.target || event.srcElement;110 },111 112 /**113 * 计算div的偏移量114 * @param obj115 * @returns {{left: (Number|number), top: (Number|number)}}116 */117 getOffset: function(obj) {118 var x = obj.offsetLeft || 0;119 var y = obj.offsetTop || 0;120 var temp = obj;121 while (temp.offsetParent) {122 temp = temp.offsetParent;123 x += temp.offsetLeft;124 y += temp.offsetTop;125 }126 //alert("x:"+x+" y:"+y);127 return { left: x, top: y };128 },129 130 /**131 * 将tagetDiv定位到sourceDiv下方,与sourceDic左对齐,宽度一致132 * @param sourceDiv133 * @param targetDiv134 */135 positionDiv: function(sourceDiv, targetDiv) {136 var obj = document.getElementById(sourceDiv);137 var xy = this.getOffset(obj);138 $("#" + targetDiv).CSS("left", xy.left);139 $("#" + targetDiv).css("width", $("#" + sourceDiv).outerWidth());140 $("#" + targetDiv).css("top", (xy.top + $("#" + sourceDiv).outerHeight()));141 142 },143 144 init: function() {145 var control = document.getElementById(this.controlId);146 var results = document.getElementById(this.resultDivId);147 var jControl = $(control);148 var jResults = $(results);149 var autoThisObj = this;150 151 document.onclick = function(event) {152 //$("#"+resultDivId).hide();153 var target = autoThisObj.getTarget(autoThisObj.getEvent(event));154 //alert(target.id);155 if (target.id == autoThisObj.controlId) {156 return false;157 }158 autoThisObj.clearResults();159 }160 161 //兼容ie(ie浏览器下,当按下up与down键时,输入框会失去焦点,导致up与down键不起作用)162 jResults.bind("keydown", function(event) {163 jControl.keydown();164 return false;165 });166 167 168 //给指定控件绑定keyup事件169 $("#" + autoThisObj.controlId).bind("keyup", function(event) {170 var e = autoThisObj.getEvent(event);171 var keyCode = e.keyCode;172 if ((keyCode == '40' || keyCode == '38' || keyCode == '37' || keyCode == '39' || keyCode == '13' || keyCode == '9')) {173 return false;174 }175 176 autoThisObj.index = -1;177 results.scrollTop = 0;178 179 var keyWord = $.trim(jControl.val());180 if (keyword.length == 0) {181 //jResults.hide();182 autoThisObj.clearResults();183 return;184 }185 186 //获取动态数据集,自定义函数的优先级最高187 var autoDatas = autoThisObj.getCompleteDatas();//调用自定义数据获取函数188 if( (autoDatas instanceof Array) && (autoDatas.length > 0) ){ 189 autoThisObj.dynamicDatas = autoDatas;190 }191 else if(autoThisObj.serverEnabled=="true"){ //服务器端获取数据192 autoThisObj.getAjaxDatas(); 193 }194 195 //196 if(autoThisObj.dynamicDatas!=null){197 autoThisObj.generateHtml(autoThisObj.dynamicDatas);198 }199 else if(autoThisObj.useStaticDatas=="true" && autoThisObj.datas.length>0){200 autoThisObj.generateHtml(autoThisObj.datas);201 } 261 }); //end keyup()262 263 this.navigate();264 }, // end init()265 266 /**267 * 定义 up与down 按键功能268 * @param event269 * @param objectId270 * @returns {boolean}271 */272 navigate: function(event) {273 274 var control = document.getElementById(this.controlId);275 var results = document.getElementById(this.resultDivId);276 var jControl = $(control);277 var jResults = $(results);278 279 var autoThisObj = this;280 281 this.keyDownBind(jControl);282 283 }, // end navigate()284 285 /**286 * 给指定jquery元素绑定keydown事件,使其可以进行匹配项的选择287 */288 keyDownBind: function(jObject) {289 var control = document.getElementById(this.controlId);290 var results = document.getElementById(this.resultDivId);291 var jControl = $(control);292 var jResults = $(results);293 var autoThisObj = this;294 jObject.keydown(function(e
新闻热点
疑难解答