<script> //parameter must be a Array. function Iterator(arr){ this.data = arr; this.size = arr.length; this.position = 0; } Iterator.prototype.rewind = function(){ this.position = 0; return this; }; Iterator.prototype.getKey = function(){ return this.position; }; Iterator.prototype.current = function(){ return this.data[this.position]; }; Iterator.prototype.next = function(){ this.position = (this.position + 1) % this.size; return this; }; Iterator.prototype.pre = function(){ if (this.position < 0){ this.position = 0; } this.position = (this.size + this.position - 1) % this.size; return this; }; Iterator.prototype.valid = function (){ return this.position < this.size && this.position >= 0; }; //Polyfill of IE if (!Array.prototype.includes) { Array.prototype.includes = function(searchElement, fromIndex) { if (this == null) { throw new TypeError('"this" is null or not defined'); } var o = Object(this); var len = o.length >>> 0; if (len === 0) { return false; } var n = fromIndex | 0; var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); while (k < len) { if (o[k] === searchElement) { return true; } k++; } return false; }; } jQuery(function(){ var $$ = jQuery, ENTER_KEY = 13, UP_KEY = 38, DOWN_KEY = 40; var blackListOfKey = [ENTER_KEY, UP_KEY, DOWN_KEY, 37, 39]; var ModelFinder = { searchInput: $$('#search_input'), goBtn: $$('#search_btn'), optionsContainer: $$('#search_options'), lookupUrlByName: function (name){ var url = ''; name = String(name); this.optionsContainer.find('li').each(function(){ if ($$(this).text().toLowerCase() === name.toLowerCase()) { url = $$(this).attr('data-url'); } }); return url; }, onGoBtnClick: function () { var url = this.lookupUrlByName($$.trim(this.searchInput.val())); if (!url) { alert(this.searchInput.attr('data-tips')); this.searchInput.focus(); return; } //alert(url); window.location.href = url; }, hideOptionsContainer: function (){ this.optionsContainer.hide().find('li').hide().removeClass('current'); }, onSelectedItem: function (item){ item && this.searchInput.val(item.text()); }, filterMatchedItems: function (matchStr, matchBegin) { var that = this, found = false; if (matchStr) { this.optionsContainer.find('li').each(function (){ if (matchBegin ? $$(this).text().toLowerCase().indexOf(matchStr.toLowerCase()) === 0 : $$(this).text().toLowerCase().indexOf(matchStr.toLowerCase()) > -1) { $$(this).show(); found = true; } else { $$(this).hide(); } }); } if (found) { this.optionsContainer.show().iterator = (function () { var obj = new Iterator(that.optionsContainer.find('li:visible')); var current = Iterator.prototype.current; //Overwrite method 'current' of Iterator obj.current = function (){ var curEle = current.apply(this, arguments); curEle['scrollIntoView'] && curEle.scrollIntoView({behavior: 'smooth'}); that.optionsContainer.find('li').removeClass('current'); return $$(curEle).addClass('current'); }; obj.position = -1; that.optionsContainer.find('li').removeClass('current'); return obj; })(); } else { this.hideOptionsContainer(); } } }; ModelFinder.goBtn.on('click', function(){ ModelFinder.onGoBtnClick(); }); ModelFinder.searchInput.on('keyup', debounce(function (e) { var value = $$.trim(this.value), keyCode = e.keyCode, DO_MATCH = 3; if (blackListOfKey.includes(keyCode) || value.length < DO_MATCH) { value.length < DO_MATCH && ModelFinder.hideOptionsContainer(); return; } ModelFinder.filterMatchedItems(value); }, 100)); ModelFinder.searchInput.on('keydown', function(e){ var keyCode = e.keyCode; if (keyCode === ENTER_KEY || keyCode === UP_KEY || keyCode === DOWN_KEY) { return false; } }); ModelFinder.searchInput.on('click', function(e){ e.stopPropagation(); }); ModelFinder.optionsContainer.on('click', 'li', function (){ ModelFinder.onSelectedItem($$(this)); ModelFinder.hideOptionsContainer(); }); $$(document).on('click', function(){ ModelFinder.hideOptionsContainer(); }); $$('#search_input_wrapper').on('keyup', function (e){ var keyCode = e.keyCode; switch (keyCode) { case ENTER_KEY: /* if (ModelFinder.optionsContainer.iterator.valid()) { ModelFinder.onSelectedItem(ModelFinder.optionsContainer.iterator.current()); ModelFinder.hideOptionsContainer(); }*/ ModelFinder.onGoBtnClick(); break; case UP_KEY: if (ModelFinder.optionsContainer.is(':hidden')) return; ModelFinder.onSelectedItem(ModelFinder.optionsContainer.iterator.pre().current()); break; case DOWN_KEY: if (ModelFinder.optionsContainer.is(':hidden')) return; ModelFinder.onSelectedItem(ModelFinder.optionsContainer.iterator.next().current()); break; } }); }); function debounce(fn, wait, immediate){ var timeout; return function (){ var context = this, args = arguments; var later = function (){ timeout = null; if (!immediate) { fn.apply(context, args); } }; var callNow = immediate && !timeout; clearTimeout(timeout); timeout = setTimeout(later, wait); if (callNow) { fn.apply(context, args); } }; } </script> <style> #search_input_wrapper{display:inline-block;position:relative;vertical-align:middle;} #search_options{display:none;list-style: none;position:absolute;left:0;top:38px;z-index:99;padding: 0;margin:0;overflow-y: auto;width:100%;max-height: 90px!important;background: #fff;} #search_options li{display:none;cursor:pointer;height:20px;line-height: 20px;text-align: left;padding-left: 5px;margin: 0;} #search_options li:hover{background: #C0DCF3;} #search_options li.current{background: #C0DCF3;} </style> <span> <div id="search_input_wrapper"> <input id="search_input" data-tips="Please enter a name!" value=""/> <ul id="search_options"> <li data-url="ace">Ace</li> <li data-url="boost">Boost</li> </ul> </div> <button id="search_btn" class="model-finder-btn btn btn-basic">Go</button> </span>