自动填充脚本使用及注意事项

网站开发需要,找了几个js脚本,最后选择了jQuery Autocomplete Mod(http://www.pengoworks.com/workshop/jquery/autocomplete.htm

稍作修改,改动处见中文注释

  1 jQuery.autocomplete = function(input, options) {
  2     // Create a link to self
  3     var me = this;
  4 
  5     // Create jQuery object for input element
  6     var $input = $(input).attr("autocomplete", "off");
  7 
  8     // Apply inputClass if necessary
  9     if (options.inputClass) $input.addClass(options.inputClass);
 10 
 11     // Create results
 12     var results = document.createElement("div");
 13     // Create jQuery object for results
 14     var $results = $(results);
 15     $results.hide().addClass(options.resultsClass).css("position", "absolute");
 16     if( options.width > 0 ) $results.css("width", options.width);
 17 
 18     // Add to body element
 19     $("body").append(results);
 20 
 21     input.autocompleter = me;
 22 
 23     var timeout = null;
 24     var prev = "";
 25     var active = -1;
 26     var cache = {};
 27     var keyb = false;
 28     var hasFocus = false;
 29     var lastKeyPressCode = null;
 30 
 31     // flush cache
 32     function flushCache(){
 33         cache = {};
 34         cache.data = {};
 35         cache.length = 0;
 36     };
 37 
 38     // flush cache
 39     flushCache();
 40 
 41     // if there is a data array supplied
 42     if( options.data != null ){
 43         var sFirstChar = "", stMatchSets = {}, row = [];
 44 
 45         // no url was specified, we need to adjust the cache length to make sure it fits the local data store
 46         if( typeof options.url != "string" ) options.cacheLength = 1;
 47 
 48         // loop through the array and create a lookup structure
 49         for( var i=0; i < options.data.length; i++ ){
 50             // if row is a string, make an array otherwise just reference the array
 51             row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]);
 52 
 53             // if the length is zero, don't add to list
 54             if( row[0].length > 0 ){
 55                 // get the first character
 56                 sFirstChar = row[0].substring(0, 1).toLowerCase();
 57                 // if no lookup array for this character exists, look it up now
 58                 if( !stMatchSets[sFirstChar] ) stMatchSets[sFirstChar] = [];
 59                 // if the match is a string
 60                 stMatchSets[sFirstChar].push(row);
 61             }
 62         }
 63 
 64         // add the data items to the cache
 65         for( var k in stMatchSets ){
 66             // increase the cache size
 67             options.cacheLength++;
 68             // add to the cache
 69             addToCache(k, stMatchSets[k]);
 70         }
 71     }
 72 
 73     $input.keyup(function(e) {
 74         // track last key pressed
 75         lastKeyPressCode = e.keyCode;
 76         switch(e.keyCode) {
 77             case 38: // up
 78                 e.preventDefault();
 79                 moveSelect(-1);
 80                 break;
 81             case 40: // down
 82                 e.preventDefault();
 83                 moveSelect(1);
 84                 break;
 85             case 9:  // tab
 86             case 13: // return
 87                 if( selectCurrent() ){
 88                     // make sure to blur off the current field
 89                     $input.get(0).blur();
 90                     e.preventDefault();
 91                 }
 92                 break;
 93             default:
 94                 active = -1;
 95                 if (timeout) clearTimeout(timeout);
 96                 timeout = setTimeout(function(){onChange();}, options.delay);
 97                 break;
 98         }
 99     })
100     .focus(function(){
101         // track whether the field has focus, we shouldn't process any results if the field no longer has focus
102         hasFocus = true;
103     })
104     .blur(function() {
105         // track whether the field has focus
106         hasFocus = false;
107         hideResults();
108     });
109 
110     hideResultsNow();
111 
112     function onChange() {
113         // ignore if the following keys are pressed: [del] [shift] [capslock]
114         if( lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32) ) return $results.hide();
115         var v = $input.val();
116         if (v == prev) return;
117         prev = v;
118         if (v.length >= options.minChars) {
119             $input.addClass(options.loadingClass);
120             requestData(v);
121         } else {
122             $input.removeClass(options.loadingClass);
123             $results.hide();
124         }
125     };
126 
127      function moveSelect(step) {
128 
129         var lis = $("li", results);
130         if (!lis) return;
131 
132         active += step;
133 
134         if (active < 0) {
135             active = 0;
136         } else if (active >= lis.size()) {
137             active = lis.size() - 1;
138         }
139 
140         lis.removeClass("ac_over");
141 
142         $(lis[active]).addClass("ac_over");
143 
144         // Weird behaviour in IE
145         // if (lis[active] && lis[active].scrollIntoView) {
146         //     lis[active].scrollIntoView(false);
147         // }
148 
149     };
150 
151     function selectCurrent() {
152         var li = $("li.ac_over", results)[0];
153         if (!li) {
154             var $li = $("li", results);
155             if (options.selectOnly) {
156                 if ($li.length == 1) li = $li[0];
157             } else if (options.selectFirst) {
158                 li = $li[0];
159             }
160         }
161         if (li) {
162             selectItem(li);
163             return true;
164         } else {
165             return false;
166         }
167     };
168 
169     function selectItem(li) {
170         if (!li) {
171             li = document.createElement("li");
172             li.extra = [];
173             li.selectValue = "";
174         }
175         var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML);
176         input.lastSelected = v;
177         prev = v;
178         $results.html("");
179         $input.val(v);
180         hideResultsNow();
181         if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li) }, 1);
182     };
183 
184     // selects a portion of the input string
185     function createSelection(start, end){
186         // get a reference to the input element
187         var field = $input.get(0);
188         if( field.createTextRange ){
189             var selRange = field.createTextRange();
190             selRange.collapse(true);
191             selRange.moveStart("character", start);
192             selRange.moveEnd("character", end);
193             selRange.select();
194         } else if( field.setSelectionRange ){
195             field.setSelectionRange(start, end);
196         } else {
197             if( field.selectionStart ){
198                 field.selectionStart = start;
199                 field.selectionEnd = end;
200             }
201         }
202         field.focus();
203     };
204 
205     // fills in the input box w/the first match (assumed to be the best match)
206     function autoFill(sValue){
207         // if the last user key pressed was backspace, don't autofill
208         if( lastKeyPressCode != 8 ){
209             // fill in the value (keep the case the user has typed)
210             $input.val($input.val() + sValue.substring(prev.length));
211             // select the portion of the value not typed by the user (so the next character will erase)
212             createSelection(prev.length, sValue.length);
213         }
214     };
215 
216     function showResults() {
217         // get the position of the input field right now (in case the DOM is shifted)
218         var pos = findPos(input);
219         // either use the specified width, or autocalculate based on form element
220         var iWidth = (options.width > 0) ? options.width : $input.width();
221         // reposition
222         $results.css({
223             width: parseInt(iWidth) + "px",
224             top: (pos.y + input.offsetHeight) + "px",
225             left: pos.x + "px"
226         }).show();
227     };
228 
229     function hideResults() {
230         if (timeout) clearTimeout(timeout);
231         timeout = setTimeout(hideResultsNow, 200);
232     };
233 
234     function hideResultsNow() {
235         if (timeout) clearTimeout(timeout);
236         $input.removeClass(options.loadingClass);
237         if ($results.is(":visible")) {
238             $results.hide();
239         }
240         if (options.mustMatch) {
241             var v = $input.val();
242             if (v != input.lastSelected) {
243                 selectItem(null);
244             }
245         }
246     };
247 
248     function receiveData(q, data) {
249         if (data) {
250             $input.removeClass(options.loadingClass);
251             results.innerHTML = "";
252 
253             // if the field no longer has focus or if there are no matches, do not display the drop down
254             if( !hasFocus || data.length == 0 ) return hideResultsNow();
255 
256             if ($.browser.msie) {
257                 // we put a styled iframe behind the calendar so HTML SELECT elements don't show through
258                 $results.append(document.createElement('iframe'));
259             }
260             results.appendChild(dataToDom(data));
261             // autofill in the complete box w/the first match as long as the user hasn't entered in more data
262             if( options.autoFill && ($input.val().toLowerCase() == q.toLowerCase()) ) autoFill(data[0][0]);
263             showResults();
264         } else {
265             hideResultsNow();
266         }
267     };
268 
269     function parseData(data) {
270         if (!data) return null;
271         var parsed = [];
272         var rows = data.split(options.lineSeparator);
273         for (var i=0; i < rows.length; i++) {
274             var row = $.trim(rows[i]);
275             if (row) {
276                 parsed[parsed.length] = row.split(options.cellSeparator);
277             }
278         }
279         return parsed;
280     };
281 
282     function dataToDom(data) {
283         var ul = document.createElement("ul");
284         var num = data.length;
285 
286         // limited results to a max number
287         if( (options.maxItemsToShow > 0) && (options.maxItemsToShow < num) ) num = options.maxItemsToShow;
288 
289         for (var i=0; i < num; i++) {
290             var row = data[i];
291             if (!row) continue;
292             var li = document.createElement("li");
293             if (options.formatItem) {
294                 li.innerHTML = options.formatItem(row, i, num);
295                 li.selectValue = row[0];
296             } else {
297                 li.innerHTML = row[0];
298                 li.selectValue = row[0];
299             }
300             /*var extra = null;
301             if (row.length > 1) {
302                 extra = [];
303                 for (var j=1; j < row.length; j++) {
304                     extra[extra.length] = row[j];
305                 }
306             }
307             li.extra = extra;*/
308             ul.appendChild(li);
309             $(li).hover(
310                 function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); },
311                 function() { $(this).removeClass("ac_over"); }
312             ).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) });
313         }
314         return ul;
315     };
316 
317     function requestData(q) {
318         if (!options.matchCase) q = q.toLowerCase();
319         var data = options.cacheLength ? loadFromCache(q) : null;
320         // recieve the cached data
321         if (data) {
322             receiveData(q, data);
323         // if an AJAX url has been supplied, try loading the data now
324         } else if( (typeof options.url == "string") && (options.url.length > 0) ){
325             $.get(makeUrl(q), function(data) {
326                 data=data[options.returnParam];//指定返回参数名,written by HanJunyi
327                 data = parseData(data);
328                 addToCache(q, data);
329                 receiveData(q, data);
330             });
331         // if there's been no data found, remove the loading class
332         } else {
333             $input.removeClass(options.loadingClass);
334         }
335     };
336 
337     function makeUrl(q) {
338         var url = options.url + "?q=" + encodeURI(q);
339         for (var i in options.extraParams) {
340             url += "&" + i + "=" + encodeURI(options.extraParams[i]);
341         }
342         return url;
343     };
344 
345     function loadFromCache(q) {
346         if (!q) return null;
347         if (cache.data[q]) return cache.data[q];
348         if (options.matchSubset) {
349             for (var i = q.length - 1; i >= options.minChars; i--) {
350                 var qs = q.substr(0, i);
351                 var c = cache.data[qs];
352                 if (c) {
353                     var csub = [];
354                     for (var j = 0; j < c.length; j++) {
355                         var x = c[j];
356                         var x0 = x[0];
357                         if (matchSubset(x0, q)) {
358                             csub[csub.length] = x;
359                         }
360                     }
361                     return csub;
362                 }
363             }
364         }
365         return null;
366     };
367 
368     function matchSubset(s, sub) {
369         if (!options.matchCase) s = s.toLowerCase();
370         var i = s.indexOf(sub);
371         if (i == -1) return false;
372         return i == 0 || options.matchContains;
373     };
374 
375     this.flushCache = function() {
376         flushCache();
377     };
378 
379     this.setExtraParams = function(p) {
380         options.extraParams = p;
381     };
382 
383     this.findValue = function(){
384         var q = $input.val();
385 
386         if (!options.matchCase) q = q.toLowerCase();
387         var data = options.cacheLength ? loadFromCache(q) : null;
388         if (data) {
389             findValueCallback(q, data);
390         } else if( (typeof options.url == "string") && (options.url.length > 0) ){
391             $.get(makeUrl(q), function(data) {
392                 data = parseData(data)
393                 addToCache(q, data);
394                 findValueCallback(q, data);
395             });
396         } else {
397             // no matches
398             findValueCallback(q, null);
399         }
400     }
401 
402     function findValueCallback(q, data){
403         if (data) $input.removeClass(options.loadingClass);
404 
405         var num = (data) ? data.length : 0;
406         var li = null;
407 
408         for (var i=0; i < num; i++) {
409             var row = data[i];
410 
411             if( row[0].toLowerCase() == q.toLowerCase() ){
412                 li = document.createElement("li");
413                 if (options.formatItem) {
414                     li.innerHTML = options.formatItem(row, i, num);
415                     li.selectValue = row[0];
416                 } else {
417                     li.innerHTML = row[0];
418                     li.selectValue = row[0];
419                 }
420                 var extra = null;
421                 if( row.length > 1 ){
422                     extra = [];
423                     for (var j=1; j < row.length; j++) {
424                         extra[extra.length] = row[j];
425                     }
426                 }
427                 li.extra = extra;
428             }
429         }
430 
431         if( options.onFindValue ) setTimeout(function() { options.onFindValue(li) }, 1);
432     }
433 
434     function addToCache(q, data) {
435         if (!data || !q || !options.cacheLength) return;
436         if (!cache.length || cache.length > options.cacheLength) {
437             flushCache();
438             cache.length++;
439         } else if (!cache[q]) {
440             cache.length++;
441         }
442         cache.data[q] = data;
443     };
444 
445     function findPos(obj) {
446         var curleft = obj.offsetLeft || 0;
447         var curtop = obj.offsetTop || 0;
448         while (obj = obj.offsetParent) {
449             curleft += obj.offsetLeft
450             curtop += obj.offsetTop
451         }
452         return {x:curleft,y:curtop};
453     }
454 }
455 
456 jQuery.fn.autocomplete = function(url, options, data) {
457     // Make sure options exists
458     options = options || {};
459     // Set url as option
460     options.url = url;
461     // set some bulk local data
462     options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null;
463 
464     // Set default values for required options
465     options.inputClass = options.inputClass || "ac_input";
466     options.resultsClass = options.resultsClass || "ac_results";
467     options.lineSeparator = options.lineSeparator || "\n";
468     options.cellSeparator = options.cellSeparator || "|";
469     options.minChars = options.minChars || 1;
470     options.delay = options.delay || 400;
471     options.matchCase = options.matchCase || 0;
472     options.matchSubset = options.matchSubset || 1;
473     options.matchContains = options.matchContains || 0;
474     options.cacheLength = options.cacheLength || 1;
475     options.mustMatch = options.mustMatch || 0;
476     options.extraParams = options.extraParams || {};
477     options.loadingClass = options.loadingClass || "ac_loading";
478     options.selectFirst = options.selectFirst || false;
479     options.selectOnly = options.selectOnly || false;
480     options.maxItemsToShow = options.maxItemsToShow || -1;
481     options.autoFill = options.autoFill || false;
482     options.width = parseInt(options.width, 10) || 0;
483     options.returnParam=options.returnParam||"";//指定返回参数名,written by HanJunyi
484 
485     this.each(function() {
486         var input = this;
487         new jQuery.autocomplete(input, options);
488     });
489 
490     // Don't break the chain
491     return this;
492 }
493 
494 jQuery.fn.autocompleteArray = function(data, options) {
495     return this.autocomplete(null, options, data);
496 }
497 
498 jQuery.fn.indexOf = function(e){
499     for( var i=0; i<this.length; i++ ){
500         if( this[i] == e ) return i;
501     }
502     return -1;
503 };

在页面中就这么用啦

$("#文本框id").autocomplete(url,{
    minChars:3,//用户至少需要输入三个字符数
    returnParam:"userList",//返回参数名
    formatItem: function(row, i, max) {
        return i+1 + ":" + row[0] + "[" + row[1] + "]";
    }
});

另:输入中文时自动完成功能总是用不了,alert下e.keyCode,发现抓取到的keycode总是229.Bing下找到这篇文章(http://aspok.net/html/jishuwenzhang/JavaScriptjishu/33.Html),将73行的$input.keydown改成$input.keyup就行了。

自动完成功能初步搞定。

posted @ 2012-04-10 11:10  skaco  阅读(529)  评论(0编辑  收藏  举报