对老外的加了修改,对中文的支持和条数的显示

原作者地址: http://www.devbridge.com/projects/autocomplete/

 

效果:

 autocomplete.jsView Code

 
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]}

 

弹出提示层的原型:View Code

 
<div class="autocomplete-w1">
  
<div class="autocomplete-w2">
    
<div style="width:299px;" id="Autocomplete_query" class="autocomplete">
      
<div class="selected"><strong>Li</strong>thuania<span class="number">约88个服务</span></div>
    
</div>
  
</div>
</div

 

 css控制样式自己控制:View Code

 
.autocomplete-w1 { background:url(img/shadow.png) no-repeat bottom right; position:absolute; top:4px; left:3px; /* IE6 fix: */ _background:none; _top:1px; }
.autocomplete 
{ width:300px; border:1px solid #999; background:#FFF; cursor:default; text-align:left; max-height:350px; overflow:auto; margin:-6px 6px 6px -6px; /* IE specific: */ _height:350px;  _margin:0px 6px 6px 0; overflow-x:hidden; }
.autocomplete .selected 
{ background:#F0F0F0; }
.autocomplete div 
{ padding:2px 5px; white-space:nowrap; }
.autocomplete strong 
{ font-weight:normal; color:#3399FF; }
.autocomplete .number 
{ font-weight:normal; color:red; 

作者: ╰⑥月の雨╮ 发表于 2011-07-07 16:59 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架