2015年5月12日 20:16:48 星期二
效果:
原理:
1. 建立"单字"索引(倒排索引): 将汉字拆分成单个字, 作为redis hash 的一个键, 将所有包含该字的id作为hash的值
2. 每次输入一个字, 就去redis里将该字的所有id取出来, 输入第二个字的时候, 取出第二个字的所有id与第一个字的id求交集
求得同时包含这两个字的所有id, 再进一步获取id对应的信息并返回
另外:
1. 文中还建立了两个全词索引(redis hash), 一个是 {"完整汉字词语" : "id", .....} 另一个是 {"id" : "完整汉字词语" ,.......} 方便后续程序使用, 与这个自动补全关系不大
2. 小细节, 每次点选词语的时候, js自动补上"," 而且ajax请求数据的时候, 只把最后一个","后边的用户输入词语发送给服务端
话不多说, 上代码:
js+css
1 <script type="text/javascript"> 2 var ac_domain = 'http://'+document.domain+'/'; 3 initAutoComplete(); 4 function initAutoComplete() 5 { 6 var ac_input = document.getElementById('auto_complete_input'); 7 if (!ac_input) {return false;} 8 var ac_rebuildindex = document.getElementById('rebuild_autocomplete_index'); 9 10 if (document.attachEvent) { 11 ac_input.attachEvent('oninput', input_autocomplete); //输入时进行自动补全 12 // ac_input.attachEvent('onblur', hide_autocomplete); //鼠标点击其它地方隐藏div, 屏蔽 因为onblur 和 onclick 冲突 13 ac_input.attachEvent('onfocus', input_autocomplete); //输入框重新获取焦点时显示自动补全结果 14 ac_input.attachEvent('onkeydown', input_autocomplete_keydown); //处理按键信息 15 ac_rebuildindex.attachEvent('onclick', rebuild_autocomplete_index); 16 } else { 17 ac_input.addEventListener('input', input_autocomplete); //输入时进行自动补全 18 // ac_input.addEventListener('blur', hide_autocomplete); //鼠标点击其它地方隐藏div, 屏蔽 因为onblur 和 onclick 冲突 19 ac_input.addEventListener('focus', input_autocomplete); //输入框重新获取焦点时显示自动补全结果 20 ac_input.addEventListener('keydown', input_autocomplete_keydown); //处理按键信息 21 ac_rebuildindex.addEventListener('click', rebuild_autocomplete_index); 22 } 23 24 $(document).bind("click",function(e){ 25 var target = $(e.target);//表示当前对象,切记,如果没有e这个参数,即表示整个BODY对象 26 if(target.closest(".form-input-autocomplete,#auto_complete_input").length == 0){ 27 hide_autocomplete(); 28 } 29 }) 30 } 31 32 function input_autocomplete() 33 { 34 var ac_input = document.getElementById('auto_complete_input'); 35 var userinput = ac_input.value; 36 if (!userinput) {return false;} 37 var arr_new_word = userinput.split(/,\s*/); 38 var new_word = arr_new_word.pop(); 39 40 var url = ac_domain+'getAutoComplete?word='+new_word; 41 $.get(url, 42 function(data) { 43 if (!data) {return false;}; 44 var objGame = eval('('+data+')'); 45 html = '<ul>'; 46 for (var i in objGame) { 47 html += '<li class="auto_complete_li" data->; 48 } 49 html += '</ul>'; 50 var ac_div = $("#auto_complete_div"); 51 ac_div.show(); 52 ac_div.html(html); 53 click_autocomplete_li(); //注册点击事件 54 } 55 ); 56 } 57 58 function hide_autocomplete() 59 { 60 var ac_div = document.getElementById('auto_complete_div'); 61 ac_div.style.display = 'none'; 62 } 63 64 function click_autocomplete_li() 65 { 66 var arr_ac_li = getElementsByClassName('auto_complete_li', 'ul'); 67 for (var i = 0; i < arr_ac_li.length; i++) { 68 arr_ac_li[i].addEventListener('click', function () { 69 //获取列表li上的数据, 并组装 70 var data_id = this.getAttribute('data-id'); 71 var data_name = this.getAttribute('data-name'); 72 var data_info = data_id+'|'+data_name; 73 74 ///////向输入框写数据 75 var already_input = document.getElementById('auto_complete_input'); 76 77 //去掉用户的输入, 换做用户点击的li里的name 78 var arr_already_input = already_input.value.split(/,\s*/); 79 arr_already_input.pop(); 80 arr_already_input.push(data_name); 81 82 //去重 83 var hash_already_input = {}; 84 for (var i in arr_already_input) { 85 hash_already_input[arr_already_input[i]] = arr_already_input[i]; 86 } 87 arr_already_input = []; 88 for (key in hash_already_input) { 89 arr_already_input.push(key); 90 } 91 92 //回写入表单 93 var str_already_input = arr_already_input.join(", "); 94 document.getElementById('auto_complete_input').value = str_already_input+", "; 95 96 //如果只有一条匹配, 点击后就消失, 多条的话点击后不消失, 可以多次点选 97 if (arr_ac_li.length == 1) { 98 hide_autocomplete(); 99 }; 100 }) 101 }; 102 } 103 104 function rebuild_autocomplete_index() 105 { 106 var nodeParent = this.parentNode; 107 var url = ac_domain+'buildTeamAutoComplete'; 108 $.get(url, 109 function(data) { 110 nodeParent.innerHTML = '已经重建索引,再点击输入框试试'; 111 } 112 ); 113 } 114 115 function input_autocomplete_keydown(e) 116 { 117 switch(e.keyCode){ 118 case '8': 119 //按下删除键 120 input_autocomplete(); 121 break; 122 }; 123 } 124 125 function getElementsByClassName(className, tagName) 126 { 127 var t = typeof(document.getElementsByClassName); 128 129 if (t == 'function') { 130 return document.getElementsByClassName(className); 131 } 132 133 var tags, matchedTags; 134 if (tagName) { 135 tags = document.getElementsByTagName(tagName); 136 } else { 137 tags = document.getElementsByTagName('*'); 138 } 139 140 matchedTags = []; 141 for (var i = 0; i < tags.length; i++) { 142 if (tags[i].className.indexOf(className) != -1) { 143 matchedTags.push(tags[i]); 144 } 145 } 146 147 return matchedTags; 148 } 149 </script> 150 151 <style type="text/css"> 152 /*自动补全*/ 153 .form-input-autocomplete {padding: 0px; width: 200px; border: 1px solid #aaa; display: none; z-index: 99; position: absolute; background-color: #fff; filter: alpha(opacity=100); opacity: 1;} 154 .form-input-autocomplete ul {margin: 0px;padding: 0px; text-align: left; list-style: none; font:15px;} 155 .form-input-autocomplete ul li{margin: 3px;} 156 .form-input-autocomplete ul li:hover{background-color: #eee; cursor:hand;} 157 </style>