对老外的加了修改,对中文的支持和条数的显示
原作者地址: 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, ' " οnclick="Autocomplete.instances[ ' , this .instanceId, ' ].select( ' , i, ' );" οnmοuseοver="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
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, ' " οnclick="Autocomplete.instances[ ' , this .instanceId, ' ].select( ' , i, ' );" οnmοuseοver="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> -->
// ..
}
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
< 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 ;
.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 ;