jquery.autocomplete.js运用实例
<div class="search_item_content"><b>商品搜索</b><input type="text" id="quick_search" name="q" value=""/><input type="submit" value="搜索" class="btn" /></div> 比如页面上有如上的的搜索框。。。。。那边在下面加入类似这样的代码:
jQuery(document).ready(function() {jQuery("#quick_search").autocomplete("$!{request.contextPath}/autoComplete.shtml",{max: 10,scroll: false,width: 162});})上面的autoComplete.shtml是一个struts的ACTION代码如下
public class AutoCompleteAction extends Action {public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)throws Exception {SearchSuggestionManager service = (SearchSuggestionManager) WebAppUtil.getService("searchSuggestionService", request);String keyword = request.getParameter("q");keyword=EscapeUnescape.unescape(keyword);List<SearchSuggestion> results = service.getSearchSuggestionsByKeyword(keyword);try {response.setContentType("text/html; charset=GBK");PrintWriter out = response.getWriter();JSONArray jsonArray = JSONArray.fromObject(results);out.print(jsonArray.toString());out.flush();out.close();return null;} catch (Exception e) {e.printStackTrace();return null;}}} 这个ACTION返回的是一个json类型的数组,我们用JSON来做数据的传递。格式是类似这样的
["索尼 H50","索爱"]PS:由于我们的系统是GBK的,而JQUERY的AJAX默认是UTF-8的所以我们要加入一个EscapeUnescape.java来进行格式的转换:
public class EscapeUnescape {public static String escape(String src) {int i;char j;StringBuffer tmp = new StringBuffer();tmp.ensureCapacity(src.length() * 6);for (i = 0; i < src.length(); i++) {j = src.charAt(i);if (Character.isDigit(j) || Character.isLowerCase(j)|| Character.isUpperCase(j))tmp.append(j);else if (j < 256) {tmp.append("%");if (j < 16)tmp.append("0");tmp.append(Integer.toString(j, 16));} else {tmp.append("%u");tmp.append(Integer.toString(j, 16));}}return tmp.toString();}public static String unescape(String src) {StringBuffer tmp = new StringBuffer();tmp.ensureCapacity(src.length());int lastPos = 0, pos = 0;char ch;while (lastPos < src.length()) {pos = src.indexOf("%", lastPos);if (pos == lastPos) {if (src.charAt(pos + 1) == 'u') {ch = (char) Integer.parseInt(src.substring(pos + 2, pos + 6), 16);tmp.append(ch);lastPos = pos + 6;} else {ch = (char) Integer.parseInt(src.substring(pos + 1, pos + 3), 16);tmp.append(ch);lastPos = pos + 3;}} else {if (pos == -1) {tmp.append(src.substring(lastPos));lastPos = src.length();} else {tmp.append(src.substring(lastPos, pos));lastPos = pos;}}}return tmp.toString();}public static void main(String[] args) {String stest = "中文1234 abcd[]()<+>,.~\\"; System.out.println(stest); System.out.println(escape(stest)); System.out.println(unescape(escape(stest)));}}
并且我对jquery.autocomplete.js的输入部分进行了修改,我修改后的jquery.autocomplete.js源代码如下
/* * Autocomplete - jQuery plugin 1.0.2 * * Copyright (c) 2007 Dylan Verheul, Dan G. Switzer, Anjesh Tuladhar, Jörn Zaefferer * * Dual licensed under the MIT and GPL licenses: * http://www.opensource.org/licenses/mit-license.php * http://www.gnu.org/licenses/gpl.html * * Revision: jQueryId: jquery.autocomplete.js 5747 2008-06-25 18:30:55Z joern.zaefferer $ * */;(function(jQuery) {jQuery.fn.extend({autocomplete: function(urlOrData, options) {var isUrl = typeof urlOrData == "string";options = jQuery.extend({}, jQuery.Autocompleter.defaults, {url: isUrl ? urlOrData : null,data: isUrl ? null : urlOrData,delay: isUrl ? jQuery.Autocompleter.defaults.delay : 10,max: options && !options.scroll ? 10 : 150}, options);// if highlight is set to false, replace it with a do-nothing functionoptions.highlight = options.highlight || function(value) { return value; };// if the formatMatch option is not specified, then use formatItem for backwards compatibilityoptions.formatMatch = options.formatMatch || options.formatItem;return this.each(function() {new jQuery.Autocompleter(this, options);});},result: function(handler) {return this.bind("result", handler);},search: function(handler) {return this.trigger("search", );},flushCache: function() {return this.trigger("flushCache");},setOptions: function(options){return this.trigger("setOptions", );},unautocomplete: function() {return this.trigger("unautocomplete");}});jQuery.Autocompleter = function(input, options) {var KEY = {UP: 38,DOWN: 40,DEL: 46,TAB: 9,RETURN: 13,ESC: 27,COMMA: 188,PAGEUP: 33,PAGEDOWN: 34,BACKSPACE: 8};// Create $ object for input elementvar jQueryinput = jQuery(input).attr("autocomplete", "off").addClass(options.inputClass);var timeout;var previousValue = "";var cache = jQuery.Autocompleter.Cache(options);var hasFocus = 0;var lastKeyPressCode;var config = {mouseDownOnSelect: false};var select = jQuery.Autocompleter.Select(options, input, selectCurrent, config);var blockSubmit;// prevent form submit in opera when selecting with return keyjQuery.browser.opera && jQuery(input.form).bind("submit.autocomplete", function() {if (blockSubmit) {blockSubmit = false;return false;}});// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at alljQueryinput.bind((jQuery.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {// track last key pressedlastKeyPressCode = event.keyCode;switch(event.keyCode) {case KEY.UP:event.preventDefault();if ( select.visible() ) {select.prev();} else {onChange(0, true);}break;case KEY.DOWN:event.preventDefault();if ( select.visible() ) {select.next();} else {onChange(0, true);}break;case KEY.PAGEUP:event.preventDefault();if ( select.visible() ) {select.pageUp();} else {onChange(0, true);}break;case KEY.PAGEDOWN:event.preventDefault();if ( select.visible() ) {select.pageDown();} else {onChange(0, true);}break;// matches also semicoloncase options.multiple && jQuery.trim(options.multipleSeparator) == "," && KEY.COMMA:case KEY.TAB:case KEY.RETURN:if( selectCurrent() ) {// stop default to prevent a form submit, Opera needs special handlingevent.preventDefault();blockSubmit = true;return false;}break;case KEY.ESC:select.hide();break;default:clearTimeout(timeout);timeout = setTimeout(onChange, options.delay);break;}}).focus(function(){// track whether the field has focus, we shouldn't process any// results if the field no longer has focushasFocus++;}).blur(function() {hasFocus = 0;if (!config.mouseDownOnSelect) {hideResults();}}).click(function() {// show select when clicking in a focused fieldif ( hasFocus++ > 1 && !select.visible() ) {onChange(0, true);}}).bind("search", function() {// TODO why not just specifying both arguments?var fn = (arguments.length > 1) ? arguments : null;function findValueCallback(q, data) {var result;if( data && data.length ) {for (var i=0; i < data.length; i++) {if( data.result.toLowerCase() == q.toLowerCase() ) {result = data;break;}}}if( typeof fn == "function" ) fn(result);else jQueryinput.trigger("result", result && );}jQuery.each(trimWords(jQueryinput.val()), function(i, value) {request(value, findValueCallback, findValueCallback);});}).bind("flushCache", function() {cache.flush();}).bind("setOptions", function() {jQuery.extend(options, arguments);// if we've updated the data, repopulateif ( "data" in arguments )cache.populate();}).bind("unautocomplete", function() {select.unbind();jQueryinput.unbind();jQuery(input.form).unbind(".autocomplete");});function selectCurrent() {var selected = select.selected();if( !selected )return false;var v = selected.result;previousValue = v;if ( options.multiple ) {var words = trimWords(jQueryinput.val());if ( words.length > 1 ) {v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;}v += options.multipleSeparator;}jQueryinput.val(v);hideResultsNow();jQueryinput.trigger("result", );return true;}function onChange(crap, skipPrevCheck) {if( lastKeyPressCode == KEY.DEL ) {select.hide();return;}var currentValue = jQueryinput.val();if ( !skipPrevCheck && currentValue == previousValue )return;previousValue = currentValue;currentValue = lastWord(currentValue);if ( currentValue.length >= options.minChars) {jQueryinput.addClass(options.loadingClass);if (!options.matchCase)currentValue = currentValue.toLowerCase();request(currentValue, receiveData, hideResultsNow);} else {stopLoading();select.hide();}};function trimWords(value) {if ( !value ) {return [""];}var words = value.split( options.multipleSeparator );var result = [];jQuery.each(words, function(i, value) {if ( jQuery.trim(value) )result = jQuery.trim(value);});return result;}function lastWord(value) {if ( !options.multiple )return value;var words = trimWords(value);return words;}// fills in the input box w/the first match (assumed to be the best match)// q: the term entered// sValue: the first matching resultfunction autoFill(q, sValue){// autofill in the complete box w/the first match as long as the user hasn't entered in more data// if the last user key pressed was backspace, don't autofillif( options.autoFill && (lastWord(jQueryinput.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {// fill in the value (keep the case the user has typed)jQueryinput.val(jQueryinput.val() + sValue.substring(lastWord(previousValue).length));// select the portion of the value not typed by the user (so the next character will erase)jQuery.Autocompleter.Selection(input, previousValue.length, previousValue.length + sValue.length);}};function hideResults() {clearTimeout(timeout);timeout = setTimeout(hideResultsNow, 200);};function hideResultsNow() {var wasVisible = select.visible();select.hide();clearTimeout(timeout);stopLoading();if (options.mustMatch) {// call search and run callbackjQueryinput.search(function (result){// if no value found, clear the input boxif( !result ) {if (options.multiple) {var words = trimWords(jQueryinput.val()).slice(0, -1);jQueryinput.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );}elsejQueryinput.val( "" );}});}if (wasVisible)// position cursor at end of input fieldjQuery.Autocompleter.Selection(input, input.value.length, input.value.length);};function receiveData(q, data) {if ( data && data.length && hasFocus ) {stopLoading();select.display(data, q);autoFill(q, data.value);select.show();} else {hideResultsNow();}};function request(term, success, failure) {if (!options.matchCase)term = term.toLowerCase();var data = cache.load(term);// recieve the cached dataif (data && data.length) {success(term, data);// if an AJAX url has been supplied, try loading the data now} else if( (typeof options.url == "string") && (options.url.length > 0) ){var extraParams = {timestamp: +new Date()};jQuery.each(options.extraParams, function(key, param) {extraParams = typeof param == "function" ? param() : param;});jQuery.ajax({// try to leverage ajaxQueue plugin to abort previous requestsmode: "abort",// limit abortion to this inputport: "autocomplete" + input.name,dataType: options.dataType,url: options.url,data: jQuery.extend({q: escape(term),limit: options.max}, extraParams),success: function(data) {var parsed = options.parse && options.parse(data) || parse(data);cache.add(term, parsed);success(term, parsed);}});} else {// if we have a failure, we need to empty the list -- this prevents the the key from selecting the last successful matchselect.emptyList();failure(term);}};function parse(data) {var parsed = [];var rows = eval('('+data+')');for (var i=0; i < rows.length; i++) {var row = jQuery.trim(rows);if (row) {row = row.split("|");parsed = {data: row,value: row,result: options.formatResult && options.formatResult(row, row) || row};}}return parsed;};function stopLoading() {jQueryinput.removeClass(options.loadingClass);};};jQuery.Autocompleter.defaults = {inputClass: "ac_input",resultsClass: "ac_results",loadingClass: "ac_loading",minChars: 1,delay: 400,matchCase: false,matchSubset: true,matchContains: false,cacheLength: 10,max: 100,mustMatch: false,extraParams: {},selectFirst: true,formatItem: function(row) { return row; },formatMatch: null,autoFill: false,width: 0,multiple: false,multipleSeparator: ", ",highlight: function(value, term) {return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");}, scroll: true, scrollHeight: 180};jQuery.Autocompleter.Cache = function(options) {var data = {};var length = 0;function matchSubset(s, sub) {if (!options.matchCase) s = s.toLowerCase();var i = s.indexOf(sub);if (i == -1) return false;return i == 0 || options.matchContains;};function add(q, value) {if (length > options.cacheLength){flush();}if (!data){ length++;}data = value;}function populate(){if( !options.data ) return false;// track the matchesvar stMatchSets = {},nullData = 0;// no url was specified, we need to adjust the cache length to make sure it fits the local data storeif( !options.url ) options.cacheLength = 1;// track all options for minChars = 0stMatchSets[""] = [];// loop through the array and create a lookup structurefor ( var i = 0, ol = options.data.length; i < ol; i++ ) {var rawValue = options.data;// if rawValue is a string, make an array otherwise just reference the arrayrawValue = (typeof rawValue == "string") ? : rawValue;var value = options.formatMatch(rawValue, i+1, options.data.length);if ( value === false )continue;var firstChar = value.charAt(0).toLowerCase();// if no lookup array for this character exists, look it up nowif( !stMatchSets ) stMatchSets = [];// if the match is a stringvar row = {value: value,data: rawValue,result: options.formatResult && options.formatResult(rawValue) || value};// push the current match into the set liststMatchSets.push(row);// keep track of minChars zero itemsif ( nullData++ < options.max ) {stMatchSets[""].push(row);}};// add the data items to the cachejQuery.each(stMatchSets, function(i, value) {// increase the cache sizeoptions.cacheLength++;// add to the cacheadd(i, value);});}// populate any existing datasetTimeout(populate, 25);function flush(){data = {};length = 0;}return {flush: flush,add: add,populate: populate,load: function(q) {if (!options.cacheLength || !length)return null;/** if dealing w/local data and matchContains than we must make sure * to loop through all the data collections looking for matches */if( !options.url && options.matchContains ){// track all matchesvar csub = [];// loop through all the data grids for matchesfor( var k in data ){// don't search through the stMatchSets[""] (minChars: 0) cache// this prevents duplicatesif( k.length > 0 ){var c = data;jQuery.each(c, function(i, x) {// if we've got a match, add it to the arrayif (matchSubset(x.value, q)) {csub.push(x);}});}}return csub;} else // if the exact item exists, use itif (data){return data;} elseif (options.matchSubset) {for (var i = q.length - 1; i >= options.minChars; i--) {var c = data;if (c) {var csub = [];jQuery.each(c, function(i, x) {if (matchSubset(x.value, q)) {csub = x;}});return csub;}}}return null;}};};jQuery.Autocompleter.Select = function (options, input, select, config) {var CLASSES = {ACTIVE: "ac_over"};var listItems,active = -1,data,term = "",needsInit = true,element,list;// Create resultsfunction init() {if (!needsInit)return;element = jQuery("<div/>").hide().addClass(options.resultsClass).css("position", "absolute").appendTo(document.body);list = jQuery("<ul/>").appendTo(element).mouseover( function(event) {if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') { active = jQuery("li", list).removeClass(CLASSES.ACTIVE).index(target(event)); jQuery(target(event)).addClass(CLASSES.ACTIVE); }}).click(function(event) {jQuery(target(event)).addClass(CLASSES.ACTIVE);select();// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focusinput.focus();return false;}).mousedown(function() {config.mouseDownOnSelect = true;}).mouseup(function() {config.mouseDownOnSelect = false;});if( options.width > 0 )element.css("width", options.width);needsInit = false;} function target(event) {var element = event.target;while(element && element.tagName != "LI")element = element.parentNode;// more fun with IE, sometimes event.target is empty, just ignore it thenif(!element)return [];return element;}function moveSelect(step) {listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);movePosition(step); var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE); if(options.scroll) { var offset = 0; listItems.slice(0, active).each(function() {offset += this.offsetHeight;}); if((offset + activeItem.offsetHeight - list.scrollTop()) > list.clientHeight) { list.scrollTop(offset + activeItem.offsetHeight - list.innerHeight()); } else if(offset < list.scrollTop()) { list.scrollTop(offset); } }};function movePosition(step) {active += step;if (active < 0) {active = listItems.size() - 1;} else if (active >= listItems.size()) {active = 0;}}function limitNumberOfItems(available) {return options.max && options.max < available? options.max: available;}function fillList() {list.empty();var max = limitNumberOfItems(data.length);for (var i=0; i < max; i++) {if (!data)continue;var formatted = options.formatItem(data.data, i+1, max, data.value, term);if ( formatted === false )continue;var li = jQuery("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list);jQuery.data(li, "ac_data", data);}listItems = list.find("li");if ( options.selectFirst ) {listItems.slice(0, 1).addClass(CLASSES.ACTIVE);active = 0;}// apply bgiframe if availableif ( jQuery.fn.bgiframe )list.bgiframe();}return {display: function(d, q) {init();data = d;term = q;fillList();},next: function() {moveSelect(1);},prev: function() {moveSelect(-1);},pageUp: function() {if (active != 0 && active - 8 < 0) {moveSelect( -active );} else {moveSelect(-8);}},pageDown: function() {if (active != listItems.size() - 1 && active + 8 > listItems.size()) {moveSelect( listItems.size() - 1 - active );} else {moveSelect(8);}},hide: function() {element && element.hide();listItems && listItems.removeClass(CLASSES.ACTIVE);active = -1;},visible : function() {return element && element.is(":visible");},current: function() {return this.visible() && (listItems.filter("." + CLASSES.ACTIVE) || options.selectFirst && listItems);},show: function() {var offset = jQuery(input).offset();element.css({width: typeof options.width == "string" || options.width > 0 ? options.width : jQuery(input).width(),top: offset.top + input.offsetHeight,left: offset.left}).show(); if(options.scroll) { list.scrollTop(0); list.css({maxHeight: options.scrollHeight,overflow: 'auto'}); if(jQuery.browser.msie && typeof document.body.style.maxHeight === "undefined") {var listHeight = 0;listItems.each(function() {listHeight += this.offsetHeight;});var scrollbarsVisible = listHeight > options.scrollHeight; list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );if (!scrollbarsVisible) {// IE doesn't recalculate width when scrollbar disappearslistItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );} } }},selected: function() {var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);return selected && selected.length && jQuery.data(selected, "ac_data");},emptyList: function (){list && list.empty();},unbind: function() {element && element.remove();}};};jQuery.Autocompleter.Selection = function(field, start, end) {if( field.createTextRange ){var selRange = field.createTextRange();selRange.collapse(true);selRange.moveStart("character", start);selRange.moveEnd("character", end);selRange.select();} else if( field.setSelectionRange ){field.setSelectionRange(start, end);} else {if( field.selectionStart ){field.selectionStart = start;field.selectionEnd = end;}}field.focus();};})(jQuery); 在数据输入的时候进行了转码,这样传到后台数据库的时候就不会发生乱码的问题,这两个文件见附件
具体实例可以参看www.suning.cn的商品搜索,欢迎交流
页:
[1]