您的位置:首页 > Web前端 > JQuery

jquery Autocomplete 在ios上 中文兼容性问题

2017-07-22 15:41 471 查看
因为项目需要,使用了阿里的开放搜索,然后提示这块没有用阿里的suggest,因为搜索历史是存在redis中的。直接从大家的搜索历史中获取数据就可以了。界面提示是使用的 Autocomplete 。因为是使用的webview开发,所以后端的代码要适应电脑,ios,安卓三个端。在搞ios的时候,发现中文死活不出来,研究了一下,Autocomplete 是绑定的键盘的keydown事件。ios的键盘是可以相应这个事件的。但是上面选词栏那一行是不支持的。所以最后我修改了源码的绑定事件那一块,绑定input控件,改成了如下代码:
$input.bind(("input") + ".autocomplete", function(event)
下面附上我修改过的Autocomplete.js的源码,
因为这个东西的样式是写死在js中动态创建的。
我修改之后,基本就是在界面上修改几个class(_ac_result, _ac_ul,_ac_ul)就可以了:
<div class="_ac_result" style="display: none;">
<ul class="_ac_ul" style="max-height: 646px; overflow: auto;">
<li class="_ac_li"><strong>哈</strong>利</li>
<li class="_ac_li"><strong>哈</strong>利波特</li>
<li class="_ac_li"><strong>哈</strong>利波特与魔法石</li>
<li class="_ac_li"><strong>哈</strong>佛</li>
<li class="_ac_li"><strong>哈</strong>姆雷特</li>
<li class="_ac_li"><strong>哈</strong><strong>哈</strong></li>
<li class="_ac_li">努尔<strong>哈</strong></li>
</ul>
</div>
下面给出代码:
界面上:
<input class="search_text" maxlength="20" id="search_text" placeholder="{{ default }}" type="text" value="{{ search_text }}">
js代码:
{#自动填充开关#}function autoFill(){var opensearch_suggest = JSON.parse('{{ opensearch_suggest }}');$("#search_text").autocomplete(opensearch_suggest, {minChars: 1,//至少输入的字符数,default:1;如果设为0,在输入框内双击或者删除内容时显示列表。width: 220,//下拉框的宽度,default:input元素的宽度max: 10,//下拉项目的个数,default:10scrollHeight: 300,// 下拉框的高度, Default: 180scroll: true,//当结果集大于默认高度时,是否使用滚动条,Default: truemultiple: false,//是否允许输入多个值. Default: falseautoFill: false,// 是否自动填充. Default: falsemultipleSeparator: " ",//输入多个字符时,用来分开各个的字符. Default: ","matchCase:false,//是否开启大小写敏感selectFirst:true,// 如果设置成true,下拉列表的第一个值将被自动选择, Default: truematchSubset:true,//是否启用缓存cacheLength: 5000, //缓存的长度.即缓存多少条记录.设成1为不缓存.Default: 10delay: 250,//击键后的延迟时间(单位毫秒).Default: 远程为400 本地10mustMatch:false,//如果设置为true,只会允许匹配的结果出现在输入框,当用户输入的是非法字符时,将被清除, Default: falsematchContains:true//决定比较时是否要在字符串内部查看匹配.Default: false});}
插件修改后的源码(改动不多,主要是为了适应自己的样式,还有绑定对象):
/** Autocomplete - jQuery plugin 1.1pre** 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: $Id: jquery.autocomplete.js 5785 2008-07-12 10:37:33Z joern.zaefferer $**/;(function($) {$.fn.extend({autocomplete: function(urlOrData, options) {var isUrl = typeof urlOrData == "string";options = $.extend({}, $.Autocompleter.defaults, {url: isUrl ? urlOrData : null,data: isUrl ? null : urlOrData,delay: isUrl ? $.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 $.Autocompleter(this, options);});},result: function(handler) {return this.bind("result", handler);},search: function(handler) {return this.trigger("search", [handler]);},flushCache: function() {return this.trigger("flushCache");},setOptions: function(options){return this.trigger("setOptions", [options]);},unautocomplete: function() {return this.trigger("unautocomplete");}});$.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 $input = $(input).attr("autocomplete", "off").addClass('_ac_inputClass');var timeout;var previousValue = "";var cache = $.Autocompleter.Cache(options);var hasFocus = 0;var lastKeyPressCode;var config = {mouseDownOnSelect: false};var select = $.Autocompleter.Select(options, input, selectCurrent, config);var blockSubmit;// prevent form submit in opera when selecting with return key$.browser.opera && $(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 all$input.bind(("input") + ".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 && $.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[1] : null;function findValueCallback(q, data) {var result;if( data && data.length ) {for (var i=0; i < data.length; i++) {if( data[i].result.toLowerCase() == q.toLowerCase() ) {result = data[i];break;}}}if( typeof fn == "function" ) fn(result);else $input.trigger("result", result && [result.data, result.value]);}$.each(trimWords($input.val()), function(i, value) {request(value, findValueCallback, findValueCallback);});}).bind("flushCache", function() {cache.flush();}).bind("setOptions", function() {$.extend(options, arguments[1]);// if we've updated the data, repopulateif ( "data" in arguments[1] )cache.populate();})function selectCurrent() {var selected = select.selected();if( !selected )return false;var v = selected.result;previousValue = v;if ( options.multiple ) {var words = trimWords($input.val());if ( words.length > 1 ) {v = words.slice(0, words.length - 1).join( options.multipleSeparator ) + options.multipleSeparator + v;}v += options.multipleSeparator;}$input.val(v);hideResultsNow();$input.trigger("result", [selected.data, selected.value]);return true;}function onChange(crap, skipPrevCheck) {if( lastKeyPressCode == KEY.DEL ) {select.hide();return;}var currentValue = $input.val();if ( !skipPrevCheck && currentValue == previousValue )return;previousValue = currentValue;currentValue = lastWord(currentValue);if ( currentValue.length >= options.minChars) {$input.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 = [];$.each(words, function(i, value) {if ( $.trim(value) )result[i] = $.trim(value);});return result;}function lastWord(value) {if ( !options.multiple )return value;var words = trimWords(value);return words[words.length - 1];}// 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($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {// fill in the value (keep the case the user has typed)$input.val($input.val() + sValue.substring(lastWord(previousValue).length));// select the portion of the value not typed by the user (so the next character will erase)$.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 callback$input.search(function (result){// if no value found, clear the input boxif( !result ) {if (options.multiple) {var words = trimWords($input.val()).slice(0, -1);$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );}else$input.val( "" );}});}if (wasVisible)// position cursor at end of input field$.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[0].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()};$.each(options.extraParams, function(key, param) {extraParams[key] = typeof param == "function" ? param() : param;});$.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: $.extend({q: lastWord(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 [TAB] key from selecting the last successful matchselect.emptyList();failure(term);}};function parse(data) {var parsed = [];var rows = data.split("\n");for (var i=0; i < rows.length; i++) {var row = $.trim(rows[i]);if (row) {row = row.split("|");parsed[parsed.length] = {data: row,value: row[0],result: options.formatResult && options.formatResult(row, row[0]) || row[0]};}}return parsed;};function stopLoading() {$input.removeClass(options.loadingClass);};};$.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[0];},formatMatch: null,autoFill: false,multiple: false,multipleSeparator: ", ",highlight: function(value, term) {return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");},scroll: true,scrollHeight: 180};$.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 (options.matchContains == "word"){i = s.toLowerCase().search("\\b" + sub.toLowerCase());}if (i == -1) return false;return i == 0 || options.matchContains;};function add(q, value) {if (length > options.cacheLength){flush();}if (!data[q]){length++;}data[q] = 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[i];// if rawValue is a string, make an array otherwise just reference the arrayrawValue = (typeof rawValue == "string") ? [rawValue] : 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[firstChar] )stMatchSets[firstChar] = [];// 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[firstChar].push(row);// keep track of minChars zero itemsif ( nullData++ < options.max ) {stMatchSets[""].push(row);}};// add the data items to the cache$.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[k];$.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[q]){return data[q];} else         if (options.matchSubset) {for (var i = q.length - 1; i >= options.minChars; i--) {var c = data[q.substr(0, i)];if (c) {var csub = [];$.each(c, function(i, x) {if (matchSubset(x.value, q)) {csub[csub.length] = x;}});return csub;}}}return null;}};};$.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 = $("<div/>").hide().addClass('_ac_result')//.css("position", "absolute").appendTo(document.body);list = $("<ul/>").addClass('_ac_ul').appendTo(element).mouseover( function(event) {if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));$(target(event)).addClass(CLASSES.ACTIVE);}}).click(function(event) {$(target(event)).addClass(CLASSES.ACTIVE);select();// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focusinput.focus();do_query();return false;}).mousedown(function() {config.mouseDownOnSelect = true;}).mouseup(function() {config.mouseDownOnSelect = false;});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[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {list.scrollTop(offset + activeItem[0].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[i])continue;var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);if ( formatted === false )continue;var li = $("<li/>").html( options.highlight(formatted, term) ).addClass("_ac_li").appendTo(list)[0];$.data(li, "ac_data", data[i]);}listItems = list.find("li");if ( options.selectFirst ) {listItems.slice(0, 1).addClass(CLASSES.ACTIVE);active = 0;}// apply bgiframe if availableif ( $.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)[0] || options.selectFirst && listItems[0]);},show: function() {var offset = $(input).offset();element.show();if(options.scroll) {list.scrollTop(0);list.css({maxHeight: $('._ac_result').height(),overflow: 'auto'});if($.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 disappears// listItems.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 && $.data(selected[0], "ac_data");},emptyList: function (){list && list.empty();},unbind: function() {element && element.remove();}};};$.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);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息