[转载]基于prototype的输入自动提示autocomplete – ╰⑥月の雨╮ – 博客园.
对老外的加了修改,对中文的支持和条数的显示
原作者地址: http://www.devbridge.com/projects/autocomplete/
效果:
var Autocomplete = function(el, options){ this.el = $(el); this.id = this.el.identify(); this.el.setAttribute('autocomplete','off'); this.suggestions = []; this.data = []; this.badQueries = []; this.selectedIndex = -1; this.currentValue = this.el.value; this.intervalId = 0; this.cachedResponse = []; this.instanceId = null; this.onChangeInterval = null; this.ignoreValueChange = false; this.serviceUrl = options.serviceUrl; this.numbers = [];//条数 this.options = { autoSubmit:false, minChars:1, maxHeight:300, deferRequestBy:0, width:0, showNumber:true, //是否显示条数 container:null }; if(options){ Object.extend(this.options, options); } if(Autocomplete.isDomLoaded){ this.initialize(); }else{ Event.observe(document, 'dom:loaded', this.initialize.bind(this), false); } }; Autocomplete.instances = []; Autocomplete.isDomLoaded = false; Autocomplete.getInstance = function(id){ var instances = Autocomplete.instances; var i = instances.length; while(i--){ if(instances[i].id === id){ return instances[i]; }} }; Autocomplete.highlight = function(value, re){ return value.replace(re, function(match){ return '<strong>' + match + '<\/strong>' }); }; Autocomplete.prototype = { killerFn: null, initialize: function() { var me = this; this.killerFn = function(e) { if (!$(Event.element(e)).up('.autocomplete')) { me.killSuggestions(); me.disableKillerFn(); } } .bindAsEventListener(this); if (!this.options.width) { this.options.width = this.el.getWidth(); } var div = new Element('div', { style: 'position:absolute;' }); div.update('<div class="autocomplete-w1"><div class="autocomplete-w2"><div class="autocomplete" id="Autocomplete_' + this.id + '" style="display:none; width:' + this.options.width + 'px;"></div></div></div>'); this.options.container = $(this.options.container); if (this.options.container) { this.options.container.appendChild(div); this.fixPosition = function() { }; } else { document.body.appendChild(div); } this.mainContainerId = div.identify(); this.container = $('Autocomplete_' + this.id); this.fixPosition(); Event.observe(this.el, window.opera ? 'keypress':'keydown', this.onKeyPress.bind(this)); Event.observe(this.el, 'keyup', this.onKeyUp.bind(this)); Event.observe(this.el, 'blur', this.enableKillerFn.bind(this)); Event.observe(this.el, 'focus', this.fixPosition.bind(this)); this.container.setStyle({ maxHeight: this.options.maxHeight + 'px' }); this.instanceId = Autocomplete.instances.push(this) - 1; }, fixPosition: function() { var offset = this.el.cumulativeOffset(); $(this.mainContainerId).setStyle({ top: (offset.top + this.el.getHeight()) + 'px', left: offset.left + 'px' }); }, enableKillerFn: function() { Event.observe(document.body, 'click', this.killerFn); }, disableKillerFn: function() { Event.stopObserving(document.body, 'click', this.killerFn); }, killSuggestions: function() { this.stopKillSuggestions(); this.intervalId = window.setInterval(function() { this.hide(); this.stopKillSuggestions(); } .bind(this), 300); }, stopKillSuggestions: function() { window.clearInterval(this.intervalId); }, onKeyPress: function(e) { if (!this.enabled) { return; } switch (e.keyCode) { case Event.KEY_ESC: this.el.value = this.currentValue; this.hide(); break; case Event.KEY_TAB: case Event.KEY_RETURN: if (this.selectedIndex === -1) { this.hide(); return; } this.select(this.selectedIndex); if (e.keyCode === Event.KEY_TAB) { return; } break; case Event.KEY_UP: this.moveUp(); break; case Event.KEY_DOWN: this.moveDown(); break; default: return; } Event.stop(e); }, onKeyUp: function(e) { switch (e.keyCode) { case Event.KEY_UP: case Event.KEY_DOWN: return; } clearInterval(this.onChangeInterval); if (this.currentValue !== this.el.value) { if (this.options.deferRequestBy > 0) { // Defer lookup in case when value changes very quickly: this.onChangeInterval = setInterval((function() { this.onValueChange(); }).bind(this), this.options.deferRequestBy); } else { this.onValueChange(); } } }, onValueChange: function() { clearInterval(this.onChangeInterval); this.currentValue = this.el.value; this.selectedIndex = -1; if (this.ignoreValueChange) { this.ignoreValueChange = false; return; } if (this.currentValue === '' || this.currentValue.length < this.options.minChars) { this.hide(); } else { this.getSuggestions(); } }, getSuggestions: function() { var cr = this.cachedResponse[this.currentValue]; if (cr && Object.isArray(cr.suggestions)) { this.suggestions = cr.suggestions; this.data = cr.data; this.numbers = cr.numbers; this.suggest(); } else if (!this.isBadQuery(this.currentValue)) { new Ajax.Request(this.serviceUrl, { parameters: { query: this.currentValue }, onComplete: this.processResponse.bind(this), method: 'get' }); } }, isBadQuery: function(q) { var i = this.badQueries.length; while (i--) { if (q.indexOf(this.badQueries[i]) === 0) { return true; } } return false; }, hide: function() { this.enabled = false; this.selectedIndex = -1; this.container.hide(); }, suggest: function() { if (this.suggestions.length === 0) { this.hide(); return; } var content = []; var re = new RegExp('\\b' + this.currentValue.match(/[\u4e00-\u9fa5a-zA-Z0-9]+/g).join('|\\b'), 'gi'); var numbersContent = ''; this.suggestions.each(function(value, i) { if (Object.isArray(this.numbers) && this.options.showNumber){ numbersContent = ' <span class="number">约' + this.numbers[i] + '条</span>'; } content.push((this.selectedIndex === i ? '<div class="selected"' : '<div'), ' title="', value, '" onclick="Autocomplete.instances[', this.instanceId, '].select(', i, ');" onmouseover="Autocomplete.instances[', this.instanceId, '].activate(', i, ');">', Autocomplete.highlight(value, re), numbersContent, '</div>'); } .bind(this)); this.enabled = true; this.container.update(content.join('')).show(); }, processResponse: function(xhr) { var response; try { response = xhr.responseText.evalJSON(); if (!Object.isArray(response.data)) { response.data = []; } } catch (err) { return; } this.cachedResponse[response.query] = response; if (response.suggestions.length === 0) { this.badQueries.push(response.query); } if (response.query === this.currentValue) { this.suggestions = response.suggestions; this.data = response.data; this.numbers = response.numbers; this.suggest(); } }, activate: function(index) { var divs = this.container.childNodes; var activeItem; // Clear previous selection: if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) { divs[this.selectedIndex].className = ''; } this.selectedIndex = index; if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) { activeItem = divs[this.selectedIndex] activeItem.className = 'selected'; } return activeItem; }, deactivate: function(div, index) { div.className = ''; if (this.selectedIndex === index) { this.selectedIndex = -1; } }, select: function(i) { var selectedValue = this.suggestions[i]; if (selectedValue) { this.el.value = selectedValue; if (this.options.autoSubmit && this.el.form) { this.el.form.submit(); } this.ignoreValueChange = true; this.hide(); this.onSelect(i); } }, moveUp: function() { if (this.selectedIndex === -1) { return; } if (this.selectedIndex === 0) { this.container.childNodes[0].className = ''; this.selectedIndex = -1; this.el.value = this.currentValue; return; } this.adjustScroll(this.selectedIndex - 1); }, moveDown: function() { if (this.selectedIndex === (this.suggestions.length - 1)) { return; } this.adjustScroll(this.selectedIndex + 1); }, adjustScroll: function(i) { var container = this.container; var activeItem = this.activate(i); var offsetTop = activeItem.offsetTop; var upperBound = container.scrollTop; var lowerBound = upperBound + this.options.maxHeight - 25; if (offsetTop < upperBound) { container.scrollTop = offsetTop; } else if (offsetTop > lowerBound) { container.scrollTop = offsetTop - this.options.maxHeight + 25; } this.el.value = this.suggestions[i]; }, onSelect: function(i) { (this.options.onSelect || Prototype.emptyFunction)(this.suggestions[i], this.data[i]); } }; Event.observe(document, 'dom:loaded', function(){ Autocomplete.isDomLoaded = true; }, false
使用:Event.observe(window, ‘load’, function() {
function onAutocompleteSelect(value, data){ //.. } var rand = new Date().getTime(); var url = 'data.js?r=' + rand; new Autocomplete('txtEmployeeNum', { serviceUrl: url, width: 300, //可选 onSelect: onAutocompleteSelect, //可选 showNumber: true //显示条数 //container: 'ac_container' //可选 }); }); <input type="text" name="q" id="txtEmployeeNum" /> <!-- <div id="ac_container"></div> -->
data.js 有后台控制,产生json格式数据,如下:
//{query:'z',suggestions:['z','z1','z2','z3']} //{query:'q',suggestions:['q','q1','q2','q3'],numbers:[99,88,77,66]} {query:'去',suggestions:['去','去1','去12','去123'],numbers:[99,88,77,66]}
弹出提示层的原型:
Lithuania约88个服务