Prototype-based input automatically prompts autocomplete results
Effect:
Autocomplete. js:
Copy to ClipboardReference: [www.bkjia.com] 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 = []; // number of entries
This. options = {
AutoSubmit: false,
MinChars: 1,
MaxHeight: 300,
DeferRequestBy: 0,
Width: 0,
ShowNumber: true, // whether to display the number of entries
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 font = new Element ('font', {style: 'position: absolute ;'});
Font. update ('<font class = "autocomplete-w1"> <font class = "autocomplete-w2"> <font class = "autocomplete" id = "Autocomplete _' + this. id + '"style =" display: none; width:' + this. options. width + 'px; "> </font> ');
This. options. container = $ (this. options. container );
If (this. options. container ){
This. options. container. appendChild (font );
This. fixPosition = function (){};
} Else {
Document. body. appendChild (font );
}
This. mainContainerId = font. identify ();
This. container = $ ('autocomplete _ '+ this. id );
This. fixPosition ();
Event. observe (this. el, window. opera? 'Keypress': 'keylow', 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, 'focal ', 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 = '<font class = "number">' + this. numbers [I] + '</font> ';
}
Content. push (this. selectedIndex = I? '<Font class = "selected"': '<font'), 'title = "', value,'" onclick = "Autocomplete. instances [', this. instanceId, ']. select (', I,'); "onmouseover =" Autocomplete. instances [', this. instanceId, ']. activate (', I,'); "> ', Autocomplete. highlight (value, re), numbersContent, '</font> ');
}. 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 fonts = this. container. childNodes;
Var activeItem;
// Clear previous selection:
If (this. selectedIndex! =-1 & fonts. length> this. selectedIndex ){
Fonts [this. selectedIndex]. className = '';
}
This. selectedIndex = index;
If (this. selectedIndex! =-1 & fonts. length> this. selectedIndex ){
ActiveItem = fonts [this. selectedIndex]
ActiveItem. className = 'selected ';
}
Return activeItem;
},
Deactivate: function (font, index ){
Font. 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
Usage:
Copy to ClipboardReference: [www.bkjia.com] 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, // optional
OnSelect: onAutocompleteSelect, // optional
ShowNumber: true // number of entries displayed
// Container: 'ac _ iner '// optional
});
});
<Input type = "text" name = "q" id = "txtEmployeeNum"/>
<! -- <Font id = "ac_container"> </font> -->
Data. js has background control to generate json format data, as shown below:
Copy to ClipboardReference: [www.bkjia.com] // {query: 'Z', suggestions: ['Z', 'z1', 'z2', 'z3']}
// {Query: 'Q', suggestions: ['Q', 'q1', 'q2 ', 'q3'], numbers: [,]}
{Query: 'hangzhou', suggestions: ['hangzhou', 'hangzhou', '12', 'hangzhou'], numbers: [123,]}
The prototype of the prompt layer is displayed:
Copy to ClipboardReference: [www.bkjia.com] <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"> about 88 services </span> </div>
</Div>
</Div>
</Div
Css control style self-control:
Copy to ClipboardReference: [www.bkjia.com]. 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 font {padding: 2px 5px; white-space: nowrap ;}
. Autocomplete strong {font-weight: normal; color: # 3399FF ;}
. Autocomplete. number {font-weight: normal; color: red ;}