搜索框键入提示并回显准确信息
类似百度这种,大家应该也都清楚这个东西
我提供一个网上大家都在用的一个方法,整理了一下分享出来。
1.文本框和设置一个div,class=parentCls , 搜索框的class设置:class=“inputElem” , 为文本框设置一个oninput的属性。
<div class="parentCls"> <input type="text" class="inputElem" id="inputName" name="name" value="${search}" oninput="onChange(this.value)"> <input class="btn btn-dark" type="submit" value="搜索" style="margin-left: 15px;"> <input type="hidden" class="hiddenCls"/> </div>
在返回数据之前,我们先创建一个接受返回的json值的变量。
var resultData;
2.编写JS。
1 /** 2 * JS 模糊查询 3 * @param 1.当前的input add targetCls 4 * 2. 隐藏域里面统一增加同类名 叫 hiddenCls 5 * 3. 在各个父级元素上 添加类名 parentCls 6 */ 7 function AutoComplete(options) { 8 this.config = { 9 targetCls: '.inputElem', // 输入框目标元素 10 parentCls: '.parentCls', // 父级类 11 hiddenCls: '.hiddenCls', // 隐藏域input 12 searchForm: '.jqtransformdone', //form表单 13 hoverBg: 'hoverBg', // 鼠标移上去的背景 14 outBg: 'outBg', // 鼠标移下拉的背景 15 isSelectHide: true, // 点击下拉框 是否隐藏 16 url: 'search', // url接口 17 height: 0, // 默认为0 不设置的话 那么高度自适应 18 manySelect: false, // 输入框是否多选 默认false 单选 19 renderHTMLCallback: null, // keyup时 渲染数据后的回调函数 20 callback: null, // 点击某一项 提供回调 21 closedCallback: null // 点击输入框某一项x按钮时 回调函数 22 }; 23 24 this.cache = { 25 currentIndex: -1, 26 oldIndex: -1, 27 inputArrs: [] // 多选时候 输入框值放到数组里面去 28 }; 29 this.init(options); 30 } 31 32 AutoComplete.prototype = { 33 34 constructor: AutoComplete, 35 init: function (options) { 36 37 this.config = $.extend(this.config, options || {}); 38 var self = this, 39 _config = self.config, 40 _cache = self.cache; 41 42 // 鼠标点击输入框时候 43 $(_config.targetCls).each(function (index, item) { 44 45 /* 46 * 禁止 ctrl+v 和 黏贴事件 47 */ 48 $(item).unbind('paste'); 49 $(item).bind('paste', function (e) { 50 e.preventDefault(); 51 var target = e.target, 52 targetParent = $(target).closest(_config.parentCls); 53 $(this).val(''); 54 $(_config.hiddenCls, targetParent) && $(_config.hiddenCls, targetParent).val(''); 55 }); 56 57 $(item).keyup(function (e) { 58 _cache.inputArrs = []; 59 var targetVal = $.trim($(this).val()), 60 keyCode = e.keyCode, 61 elemHeight = $(this).outerHeight(), 62 elemWidth = $(this).outerWidth(); 63 64 // 如果输入框值为空的话 那么隐藏域的value清空掉 65 if (targetVal == '') { 66 var curParents = $(this).closest(_config.parentCls); 67 $(_config.hiddenCls, curParents).val(''); 68 } 69 var targetParent = $(this).parent(); 70 $(targetParent).css({'position': 'relative'}); 71 72 if ($('.auto-tips', targetParent).length == 0) { 73 // 初始化时候 动态创建下拉框容器 74 $(targetParent).append($('<div class="auto-tips hidden"></div>')); 75 $('.auto-tips', targetParent).css({ 76 'position': 'absolute', 77 'top': elemHeight, 78 'left': '0px', 79 'margin-left': '383px', 80 'z-index': 999, 81 'width': elemWidth, 82 'border': '1px solid #ccc' 83 }); 84 } 85 86 87 var curIndex = self._keyCode(keyCode); 88 if (curIndex > -1) { 89 self._keyUpAndDown(targetVal, e, targetParent); 90 } else { 91 if (targetVal != '') { 92 self._doPostAction(targetVal, targetParent); 93 } 94 95 } 96 }); 97 98 // 失去焦点时 如果没有点击 或者上下移时候 直接输入 那么当前输入框值情况 隐藏域值情况 99 $(item).blur(function (e) { 100 var target = e.target, 101 targetParent = $(target).closest(_config.parentCls); 102 if ($(this).attr('up') || $(this).attr('down')) { 103 return; 104 } else { 105 $(this).val(''); 106 $(_config.hiddenCls, targetParent).val(''); 107 } 108 }); 109 110 }); 111 112 // 阻止form表单默认enter键提交 113 $(_config.searchForm).each(function (index, item) { 114 $(item).keydown(function (e) { 115 var keyCode = e.keyCode; 116 if (keyCode == 13) { 117 return false; 118 } 119 }); 120 }); 121 122 // 点击文档 123 $(document).click(function (e) { 124 e.stopPropagation(); 125 var target = e.target, 126 tagParent = $(target).parent(), 127 attr = $(target, tagParent).closest('.auto-tips'); 128 129 var tagCls = _config.targetCls.replace(/^\./, ''); 130 131 if (attr.length > 0 || $(target, tagParent).hasClass(tagCls)) { 132 return; 133 } else { 134 $('.auto-tips').each(function (index, item) { 135 !$(item, tagParent).hasClass('hidden') && $(item, tagParent).addClass('hidden'); 136 }); 137 138 } 139 }); 140 141 var stylesheet = '.auto-tips { margin: 0 1px; list-style: none;height:auto !important;padding: 0px;position:absolute; border:1px solid #ccc; top:27px; left:0; z-index:999; width:100%;background:#fff !important;}' + 142 '.auto-tips p {overflow: hidden;margin: 1px 0;padding: 5px 5px;text-align:left;color: #666;text-decoration: none;line-height: 23px;white-space: nowrap;cursor: pointer;zoom: 1;}' + 143 '.auto-tips p img{ vertical-align:middle;float:left;}' + 144 '.auto-tips p.hoverBg {background-color: #669cb6;color: #fff;cursor: pointer;}' + 145 '.hidden {display:none;}'; 146 147 this._addStyleSheet(stylesheet); 148 149 }, 150 /** 151 * 键盘上下键操作 152 */ 153 _keyUpAndDown: function (targetVal, e, targetParent) { 154 var self = this, 155 _cache = self.cache, 156 _config = self.config; 157 158 // 如果请求成功后 返回了数据(根据元素的长度来判断) 执行以下操作 159 if ($('.auto-tips p', targetParent) && $('.auto-tips p', targetParent).length > 0) { 160 161 var plen = $('.auto-tips p', targetParent).length, 162 keyCode = e.keyCode; 163 _cache.oldIndex = _cache.currentIndex; 164 165 // 上移操作 166 if (keyCode == 38) { 167 if (_cache.currentIndex == -1) { 168 _cache.currentIndex = plen - 1; 169 } else { 170 _cache.currentIndex = _cache.currentIndex - 1; 171 if (_cache.currentIndex < 0) { 172 _cache.currentIndex = plen - 1; 173 } 174 } 175 if (_cache.currentIndex !== -1) { 176 177 !$('.auto-tips .p-index' + _cache.currentIndex, targetParent).hasClass(_config.hoverBg) && 178 $('.auto-tips .p-index' + _cache.currentIndex, targetParent).addClass(_config.hoverBg).siblings().removeClass(_config.hoverBg); 179 var curAttr = $('.auto-tips .p-index' + _cache.currentIndex, targetParent).attr('data-html'); 180 // embId = $('.auto-tips .p-index'+_cache.currentIndex,targetParent).attr('embId'); 181 182 // 判断是否是多选操作 多选操作 暂留接口 183 if (_config.manySelect) { 184 _cache.inputArrs.push(curAttr); 185 _cache.inputArrs = self._unique(_cache.inputArrs); 186 self._manySelect(targetParent); 187 } else { 188 $(_config.targetCls, targetParent).val(curAttr); 189 // 上移操作增加一个属性 当失去焦点时候 判断有没有这个属性 190 if (!$(_config.targetCls, targetParent).attr('up')) { 191 $(_config.targetCls, targetParent).attr('up', 'true'); 192 } 193 194 self._createDiv(targetParent, curAttr); 195 // hover 196 self._hover(targetParent); 197 } 198 199 } 200 201 } else if (keyCode == 40) { //下移操作 202 if (_cache.currentIndex == plen - 1) { 203 _cache.currentIndex = 0; 204 } else { 205 _cache.currentIndex++; 206 if (_cache.currentIndex > plen - 1) { 207 _cache.currentIndex = 0; 208 } 209 } 210 if (_cache.currentIndex !== -1) { 211 212 !$('.auto-tips .p-index' + _cache.currentIndex, targetParent).hasClass(_config.hoverBg) && 213 $('.auto-tips .p-index' + _cache.currentIndex, targetParent).addClass(_config.hoverBg).siblings().removeClass(_config.hoverBg); 214 var curAttr = $('.auto-tips .p-index' + _cache.currentIndex, targetParent).attr('data-html'); 215 // embId = $('.auto-tips .p-index'+_cache.currentIndex,targetParent).attr('embId'); 216 217 218 // 判断是否是多选操作 多选操作 暂留接口 219 if (_config.manySelect) { 220 _cache.inputArrs.push(curAttr); 221 _cache.inputArrs = self._unique(_cache.inputArrs); 222 self._manySelect(targetParent); 223 } else { 224 $(_config.targetCls, targetParent).val(curAttr); 225 226 // 下移操作增加一个属性 当失去焦点时候 判断有没有这个属性 227 if (!$(_config.targetCls, targetParent).attr('down')) { 228 $(_config.targetCls, targetParent).attr('down', 'true'); 229 } 230 231 var pCls = $(_config.targetCls, targetParent).closest(_config.parentCls); 232 // $(_config.hiddenCls,pCls).val(embId); 233 self._createDiv(targetParent, curAttr); 234 self._closed(targetParent); 235 // hover 236 self._hover(targetParent); 237 } 238 239 } 240 } else if (keyCode == 13) { //回车操作 241 var curVal = $('.auto-tips .p-index' + _cache.oldIndex, targetParent).attr('data-html'); 242 $(_config.targetCls, targetParent).val(curVal); 243 /* 244 if(_config.isSelectHide) { 245 !$(".auto-tips",targetParent).hasClass('hidden') && $(".auto-tips",targetParent).addClass('hidden'); 246 } 247 */ 248 249 _cache.currentIndex = -1; 250 _cache.oldIndex = -1; 251 252 } 253 } 254 }, 255 // 键码判断 256 _keyCode: function (code) { 257 var arrs = ['17', '18', '38', '40', '37', '39', '33', '34', '35', '46', '36', '13', '45', '44', '145', '19', '20', '9']; 258 for (var i = 0, ilen = arrs.length; i < ilen; i++) { 259 if (code == arrs[i]) { 260 return i; 261 } 262 } 263 return -1; 264 }, 265 _doPostAction: function (targetVal, targetParent) { 266 267 var self = this, 268 _cache = self.cache, 269 _config = self.config, 270 url = _config.url; 271 272 // 假如返回的数据如下: 273 if (resultData != null) { 274 var result = JSON.parse(resultData); 275 if (result.length > 0) { 276 var results = result; 277 self._renderHTML(results, targetParent); 278 self._executeClick(results, targetParent); 279 } else { 280 !$('.auto-tips', targetParent).hasClass('hidden') && $('.auto-tips', targetParent).addClass("hidden"); 281 $('.auto-tips', targetParent).html(''); 282 } 283 } 284 }, 285 286 /* 287 $.get(url+"?keyword="+targetVal+"×tamp="+new Date().getTime(),function(data){ 288 var ret = $.parseJSON(data.content), 289 results = ret.results; 290 if(results.length > 0) { 291 self._renderHTML(results,targetParent); 292 self._executeClick(results,targetParent); 293 }else { 294 !$('.auto-tips',targetParent).hasClass('hidden') && $('.auto-tips',targetParent).addClass("hidden"); 295 $('.auto-tips',targetParent).html(''); 296 297 } 298 });*/ 299 _renderHTML: function (ret, targetParent) { 300 var self = this, 301 _config = self.config, 302 _cache = self.cache, 303 html = ''; 304 305 for (var i = 0, ilen = ret.length; i < ilen; i += 1) { 306 html += '<p data-html = "' + ret[i].appname + '" class="p-index' + i + '">' + 307 '<span>' + ret[i].appname + '</span>' + 308 '</p>'; 309 } 310 // 渲染值到下拉框里面去 311 $('.auto-tips', targetParent).html(html); 312 $('.auto-tips', targetParent).hasClass('hidden') && $('.auto-tips', targetParent).removeClass('hidden'); 313 $('.auto-tips p:last', targetParent).css({"border-bottom": 'none'}); 314 315 316 // 出现滚动条 计算p的长度 * 一项p的高度 是否大于 设置的高度 如是的话 出现滚动条 反之 317 var plen = $('.auto-tips p', targetParent).length, 318 pheight = $('.auto-tips p', targetParent).height(); 319 320 if (_config.height > 0) { 321 if (plen * pheight > _config.height) { 322 $('.auto-tips', targetParent).css({'height': _config.height, 'overflow': 'auto'}); 323 } else { 324 $('.auto-tips', targetParent).css({'height': 'auto', 'overflow': 'auto'}); 325 } 326 } 327 } 328 , 329 /** 330 * 当数据相同的时 点击对应的项时 返回数据 331 */ 332 _executeClick: function (ret, targetParent) { 333 var self = this, 334 _config = self.config, 335 _cache = self.cache; 336 $('.auto-tips p', targetParent).unbind('click'); 337 $('.auto-tips p', targetParent).bind('click', function (e) { 338 var dataAttr = $(this).attr('data-html'); 339 // embId = $(this).attr('embId'); 340 341 // 判断是否多选 342 if (_config.manySelect) { 343 _cache.inputArrs.push(dataAttr); 344 _cache.inputArrs = self._unique(_cache.inputArrs); 345 self._manySelect(targetParent); 346 } else { 347 $(_config.targetCls, targetParent).val(dataAttr); 348 var parentCls = $(_config.targetCls, targetParent).closest(_config.parentCls), 349 hiddenCls = $(_config.hiddenCls, parentCls); 350 // $(hiddenCls).val(embId); 351 self._createDiv(targetParent, dataAttr); 352 353 // hover 354 self._hover(targetParent); 355 356 // !$(_config.targetCls,targetParent).hasClass('hidden') && $(_config.targetCls,targetParent).addClass('hidden'); 357 } 358 self._closed(targetParent); 359 if (_config.isSelectHide) { 360 !$('.auto-tips', targetParent).hasClass('hidden') && $('.auto-tips', targetParent).addClass('hidden'); 361 } 362 _config.callback && $.isFunction(_config.callback) && _config.callback(); 363 }); 364 365 // 鼠标移上效果 366 $('.auto-tips p', targetParent).hover(function (e) { 367 !$(this, targetParent).hasClass(_config.hoverBg) && 368 $(this, targetParent).addClass(_config.hoverBg).siblings().removeClass(_config.hoverBg); 369 }); 370 } 371 , 372 _hover: function (targetParent) { 373 $('.create-input span', targetParent).hover(function () { 374 $(this).css({"background": '#ccc', 'padding-left': '0px'}); 375 }, function () { 376 $(this).css({"background": ''}); 377 }); 378 } 379 , 380 // 动态的创建div标签 遮住input输入框 381 _createDiv: function (targetParent, dataAttr) { 382 var self = this, 383 _config = self.config; 384 var iscreate = $('.create-input', targetParent); 385 386 // 确保只创建一次div 387 if (iscreate.length > 0) { 388 $('.create-input', targetParent).remove(); 389 } 390 $(_config.targetCls, targetParent).val(dataAttr); 391 $('.alink', targetParent).css({'float': 'left', 'background': 'none'}); 392 } 393 , 394 // X 关闭事件 395 _closed: function (targetParent) { 396 var self = this, 397 _config = self.config; 398 /* 399 * 点击X 关闭按钮 400 * 判断当前输入框有没有up和down属性 有的话 删除掉 否则 什么都不做 401 */ 402 $('.alink', targetParent).click(function () { 403 $('.create-input', targetParent) && $('.create-input', targetParent).remove(); 404 $(_config.targetCls, targetParent) && $(_config.targetCls, targetParent).hasClass('hidden') && 405 $(_config.targetCls, targetParent).removeClass('hidden'); 406 $(_config.targetCls, targetParent).val(''); 407 //清空隐藏域的值 408 var curParent = $(_config.targetCls, targetParent).closest(_config.parentCls); 409 $(_config.hiddenCls, curParent).val(''); 410 411 var targetInput = $(_config.targetCls, targetParent); 412 if ($(targetInput).attr('up') || $(targetInput).attr('down')) { 413 $(targetInput).attr('up') && $(targetInput).removeAttr('up'); 414 $(targetInput).attr('down') && $(targetInput).removeAttr('down'); 415 } 416 _config.closedCallback && $.isFunction(_config.closedCallback) && _config.closedCallback(); 417 }); 418 } 419 , 420 /* 421 * 数组去重复 422 */ 423 _unique: function (arrs) { 424 var obj = {}, 425 newArrs = []; 426 for (var i = 0, ilen = arrs.length; i < ilen; i++) { 427 if (obj[arrs[i]] != 1) { 428 newArrs.push(arrs[i]); 429 obj[arrs[i]] = 1; 430 } 431 } 432 return newArrs; 433 } 434 , 435 /* 436 * 输入框多选操作 437 */ 438 _manySelect: function (targetParent) { 439 var self = this, 440 _config = self.config, 441 _cache = self.cache; 442 if (_cache.inputArrs.length > 0) { 443 $(_config.targetCls, targetParent).val(_cache.inputArrs.join(',')); 444 } 445 } 446 , 447 /* 448 * 判断是否是string 449 */ 450 _isString: function (str) { 451 return Object.prototype.toString.apply(str) === '[object String]'; 452 } 453 , 454 /* 455 * JS 动态添加css样式 456 */ 457 _addStyleSheet: function (refWin, cssText, id) { 458 459 var self = this; 460 if (self._isString(refWin)) { 461 id = cssText; 462 cssText = refWin; 463 refWin = window; 464 } 465 refWin = $(refWin); 466 var doc = document; 467 var elem; 468 469 if (id && (id = id.replace('#', ''))) { 470 elem = $('#' + id, doc); 471 } 472 473 // 仅添加一次,不重复添加 474 if (elem) { 475 return; 476 } 477 //elem = $('<style></style>'); 不能这样创建 IE8有bug 478 elem = document.createElement("style"); 479 // 先添加到 DOM 树中,再给 cssText 赋值,否则 css hack 会失效 480 $('head', doc).append(elem); 481 482 if (elem.styleSheet) { // IE 483 elem.styleSheet.cssText = cssText; 484 } else { // W3C 485 elem.appendChild(doc.createTextNode(cssText)); 486 } 487 } 488 , 489 /* 490 * 销毁操作 释放内存 491 */ 492 destory: function () { 493 var self = this, 494 _config = self.config, 495 _cache = self.cache; 496 _cache.ret = []; 497 _cache.currentIndex = 0; 498 _cache.oldIndex = 0; 499 _cache.inputArrs = []; 500 _config.targetCls = null; 501 } 502 }; 503 // 初始化 504 $(function () { 505 var auto = new AutoComplete({}); 506 }); 507 </script>
73行-83行,我手动设置了下他的位置,让他加载生成到我的搜索框下面。正好对准。
3:编写ajax:
如果监测到搜索框内容改变 就请求action
function onChange(value) { var textValue = $("#inputName").val(); //文本框的值 if ($.trim(textValue) == "") { //判断文本框的值是否为空 return; } $.ajax({ dataType: "text", type: "POST", url: "/search", //请求地址 data: {"search": textValue}, //附带参数 success: function (result) { //返回结果 var result = JSON.parse(result); //将一个json字符串转换成对象 if (result.status == 1) { //判断返回值 resultData = result.data; //将返回的json信息,赋值给我们上面定义好的变量resultData } } }); }
4.返回:resutData:
将返回的resultData,传给我们上面编写的js里面。(在273行有体现)
效果:
我的sql :
select 搜索词 from 表名 where 搜索词 like CONCAT('','${搜索词}','%' ) GROUP BY 搜索词 LIMIT 5
我是以匹配以什么内容开头去查询的信息, limit 5 是控制显示条数。
你配不上自己的野心 也辜负了所受的苦难