jquery.AutoComplete.js中文修正版(支持firefox)


Posted in Javascript onApril 09, 2010
jQuery.autocomplete = function(input, options) { 
// Create a link to self 
var me = this; 
// Create jQuery object for input element 
var $input = $(input).attr("autocomplete", "off"); 
// Apply inputClass if necessary 
if (options.inputClass) $input.addClass(options.inputClass); 
// Create results 
var results = document.createElement("div"); 
// Create jQuery object for results 
var $results = $(results); 
$results.hide().addClass(options.resultsClass).css("position", "absolute"); 
if (options.width > 0) $results.css("width", options.width); 
// Add to body element 
$("body").append(results); 
input.autocompleter = me; 
var timeout = null; 
var prev = ""; 
var active = -1; 
var cache = {}; 
var keyb = false; 
var hasFocus = false; 
var lastKeyPressCode = null; 
// flush cache 
function flushCache() { 
cache = {}; 
cache.data = {}; 
cache.length = 0; 
}; 
// flush cache 
flushCache(); 
// if there is a data array supplied 
if (options.data != null) { 
var sFirstChar = "", stMatchSets = {}, row = []; 
// no url was specified, we need to adjust the cache length to make sure it fits the local data store 
if (typeof options.url != "string") options.cacheLength = 1; 
// loop through the array and create a lookup structure 
for (var i = 0; i < options.data.length; i++) { 
// if row is a string, make an array otherwise just reference the array 
row = ((typeof options.data[i] == "string") ? [options.data[i]] : options.data[i]); 
// if the length is zero, don't add to list 
if (row[0].length > 0) { 
// get the first character 
sFirstChar = row[0].substring(0, 1).toLowerCase(); 
// if no lookup array for this character exists, look it up now 
if (!stMatchSets[sFirstChar]) stMatchSets[sFirstChar] = []; 
// if the match is a string 
stMatchSets[sFirstChar].push(row); 
} 
} 
// add the data items to the cache 
for (var k in stMatchSets) { 
// increase the cache size 
options.cacheLength++; 
// add to the cache 
addToCache(k, stMatchSets[k]); 
} 
} 
$input 
.keydown(function(e) { 
// track last key pressed 
lastKeyPressCode = e.keyCode; 
switch (e.keyCode) { 
case 38: // up 
e.preventDefault(); 
moveSelect(-1); 
break; 
case 40: // down 
e.preventDefault(); 
moveSelect(1); 
break; 
case 9: // tab 
case 13: // return 
if (selectCurrent()) { 
// make sure to blur off the current field 
$input.get(0).blur(); 
e.preventDefault(); 
} 
break; 
default: 
active = -1; 
if (timeout) clearTimeout(timeout); 
timeout = setTimeout(function() { onChange(); }, options.delay); 
break; 
} 
}) 
.focus(function() { 
// track whether the field has focus, we shouldn't process any results if the field no longer has focus 
hasFocus = true; 
}) 
.blur(function() { 
// track whether the field has focus 
hasFocus = false; 
hideResults(); 
}) 
.bind("input", function() { 
// @hack:support for inputing chinese characters in firefox 
onChange(0, true); 
}); 
hideResultsNow(); 
function onChange() { 
// ignore if the following keys are pressed: [del] [shift] [capslock] 
if (lastKeyPressCode == 46 || (lastKeyPressCode > 8 && lastKeyPressCode < 32)) return $results.hide(); 
var v = $input.val(); 
if (v == prev) return; 
prev = v; 
if (v.length >= options.minChars) { 
$input.addClass(options.loadingClass); 
requestData(v); 
} else { 
$input.removeClass(options.loadingClass); 
$results.hide(); 
} 
}; 
function moveSelect(step) { 
var lis = $("li", results); 
if (!lis) return; 
active += step; 
if (active < 0) { 
active = 0; 
} else if (active >= lis.size()) { 
active = lis.size() - 1; 
} 
lis.removeClass("ac_over"); 
$(lis[active]).addClass("ac_over"); 
// Weird behaviour in IE 
// if (lis[active] && lis[active].scrollIntoView) { 
// lis[active].scrollIntoView(false); 
// } 
}; 
function selectCurrent() { 
var li = $("li.ac_over", results)[0]; 
if (!li) { 
var $li = $("li", results); 
if (options.selectOnly) { 
if ($li.length == 1) li = $li[0]; 
} else if (options.selectFirst) { 
li = $li[0]; 
} 
} 
if (li) { 
selectItem(li); 
return true; 
} else { 
return false; 
} 
}; 
function selectItem(li) { 
if (!li) { 
li = document.createElement("li"); 
li.extra = []; 
li.selectValue = ""; 
} 
var v = $.trim(li.selectValue ? li.selectValue : li.innerHTML); 
input.lastSelected = v; 
prev = v; 
$results.html(""); 
$input.val(v); 
hideResultsNow(); 
if (options.onItemSelect) setTimeout(function() { options.onItemSelect(li) }, 1); 
}; 
// selects a portion of the input string 
function createSelection(start, end) { 
// get a reference to the input element 
var field = $input.get(0); 
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(); 
}; 
// fills in the input box w/the first match (assumed to be the best match) 
function autoFill(sValue) { 
// if the last user key pressed was backspace, don't autofill 
if (lastKeyPressCode != 8) { 
// fill in the value (keep the case the user has typed) 
$input.val($input.val() + sValue.substring(prev.length)); 
// select the portion of the value not typed by the user (so the next character will erase) 
createSelection(prev.length, sValue.length); 
} 
}; 
function showResults() { 
// get the position of the input field right now (in case the DOM is shifted) 
var pos = findPos(input); 
// either use the specified width, or autocalculate based on form element 
var iWidth = (options.width > 0) ? options.width : $input.width(); 
// reposition 
$results.css({ 
width: parseInt(iWidth) + "px", 
top: (pos.y + input.offsetHeight) + "px", 
left: pos.x + "px" 
}).show(); 
}; 
function hideResults() { 
if (timeout) clearTimeout(timeout); 
timeout = setTimeout(hideResultsNow, 200); 
}; 
function hideResultsNow() { 
if (timeout) clearTimeout(timeout); 
$input.removeClass(options.loadingClass); 
if ($results.is(":visible")) { 
$results.hide(); 
} 
if (options.mustMatch) { 
var v = $input.val(); 
if (v != input.lastSelected) { 
selectItem(null); 
} 
} 
}; 
function receiveData(q, data) { 
if (data) { 
$input.removeClass(options.loadingClass); 
results.innerHTML = ""; 
// if the field no longer has focus or if there are no matches, do not display the drop down 
if (!hasFocus || data.length == 0) return hideResultsNow(); 
if ($.browser.msie) { 
// we put a styled iframe behind the calendar so HTML SELECT elements don't show through 
$results.append(document.createElement('iframe')); 
} 
results.appendChild(dataToDom(data)); 
// autofill in the complete box w/the first match as long as the user hasn't entered in more data 
if (options.autoFill && ($input.val().toLowerCase() == q.toLowerCase())) autoFill(data[0][0]); 
showResults(); 
} else { 
hideResultsNow(); 
} 
}; 
function parseData(data) { 
if (!data) return null; 
var parsed = []; 
var rows = data.split(options.lineSeparator); 
for (var i = 0; i < rows.length; i++) { 
var row = $.trim(rows[i]); 
if (row) { 
parsed[parsed.length] = row.split(options.cellSeparator); 
} 
} 
return parsed; 
}; 
function dataToDom(data) { 
var ul = document.createElement("ul"); 
var num = data.length; 
// limited results to a max number 
if ((options.maxItemsToShow > 0) && (options.maxItemsToShow < num)) num = options.maxItemsToShow; 
for (var i = 0; i < num; i++) { 
var row = data[i]; 
if (!row) continue; 
var li = document.createElement("li"); 
if (options.formatItem) { 
li.innerHTML = options.formatItem(row, i, num); 
li.selectValue = row[0]; 
} else { 
li.innerHTML = row[0]; 
li.selectValue = row[0]; 
} 
var extra = null; 
if (row.length > 1) { 
extra = []; 
for (var j = 1; j < row.length; j++) { 
extra[extra.length] = row[j]; 
} 
} 
li.extra = extra; 
ul.appendChild(li); 
$(li).hover( 
function() { $("li", ul).removeClass("ac_over"); $(this).addClass("ac_over"); active = $("li", ul).indexOf($(this).get(0)); }, 
function() { $(this).removeClass("ac_over"); } 
).click(function(e) { e.preventDefault(); e.stopPropagation(); selectItem(this) }); 
} 
return ul; 
}; 
function requestData(q) { 
if (!options.matchCase) q = q.toLowerCase(); 
var data = options.cacheLength ? loadFromCache(q) : null; 
// recieve the cached data 
if (data) { 
receiveData(q, data); 
// if an AJAX url has been supplied, try loading the data now 
} else if ((typeof options.url == "string") && (options.url.length > 0)) { 
$.get(makeUrl(q), function(data) { 
data = parseData(data); 
addToCache(q, data); 
receiveData(q, data); 
}); 
// if there's been no data found, remove the loading class 
} else { 
$input.removeClass(options.loadingClass); 
} 
}; 
function makeUrl(q) { 
var url = options.url + "?q=" + escape(q); 
for (var i in options.extraParams) { 
url += "&" + i + "=" + escape(options.extraParams[i]); 
} 
return url; 
}; 
function loadFromCache(q) { 
if (!q) return null; 
if (cache.data[q]) return cache.data[q]; 
if (options.matchSubset) { 
for (var i = q.length - 1; i >= options.minChars; i--) { 
var qs = q.substr(0, i); 
var c = cache.data[qs]; 
if (c) { 
var csub = []; 
for (var j = 0; j < c.length; j++) { 
var x = c[j]; 
var x0 = x[0]; 
if (matchSubset(x0, q)) { 
csub[csub.length] = x; 
} 
} 
return csub; 
} 
} 
} 
return null; 
}; 
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; 
}; 
this.flushCache = function() { 
flushCache(); 
}; 
this.setExtraParams = function(p) { 
options.extraParams = p; 
}; 
this.findValue = function() { 
var q = $input.val(); 
if (!options.matchCase) q = q.toLowerCase(); 
var data = options.cacheLength ? loadFromCache(q) : null; 
if (data) { 
findValueCallback(q, data); 
} else if ((typeof options.url == "string") && (options.url.length > 0)) { 
$.get(makeUrl(q), function(data) { 
data = parseData(data) 
addToCache(q, data); 
findValueCallback(q, data); 
}); 
} else { 
// no matches 
findValueCallback(q, null); 
} 
} 
function findValueCallback(q, data) { 
if (data) $input.removeClass(options.loadingClass); 
var num = (data) ? data.length : 0; 
var li = null; 
for (var i = 0; i < num; i++) { 
var row = data[i]; 
if (row[0].toLowerCase() == q.toLowerCase()) { 
li = document.createElement("li"); 
if (options.formatItem) { 
li.innerHTML = options.formatItem(row, i, num); 
li.selectValue = row[0]; 
} else { 
li.innerHTML = row[0]; 
li.selectValue = row[0]; 
} 
var extra = null; 
if (row.length > 1) { 
extra = []; 
for (var j = 1; j < row.length; j++) { 
extra[extra.length] = row[j]; 
} 
} 
li.extra = extra; 
} 
} 
if (options.onFindValue) setTimeout(function() { options.onFindValue(li) }, 1); 
} 
function addToCache(q, data) { 
if (!data || !q || !options.cacheLength) return; 
if (!cache.length || cache.length > options.cacheLength) { 
flushCache(); 
cache.length++; 
} else if (!cache[q]) { 
cache.length++; 
} 
cache.data[q] = data; 
}; 
function findPos(obj) { 
var curleft = obj.offsetLeft || 0; 
var curtop = obj.offsetTop || 0; 
while (obj = obj.offsetParent) { 
curleft += obj.offsetLeft 
curtop += obj.offsetTop 
} 
return { x: curleft, y: curtop }; 
} 
} 
jQuery.fn.autocomplete = function(url, options, data) { 
// Make sure options exists 
options = options || {}; 
// Set url as option 
options.url = url; 
// set some bulk local data 
options.data = ((typeof data == "object") && (data.constructor == Array)) ? data : null; 
// Set default values for required options 
options.inputClass = options.inputClass || "ac_input"; 
options.resultsClass = options.resultsClass || "ac_results"; 
options.lineSeparator = options.lineSeparator || "\n"; 
options.cellSeparator = options.cellSeparator || "|"; 
options.minChars = options.minChars || 1; 
options.delay = options.delay || 400; 
options.matchCase = options.matchCase || 0; 
options.matchSubset = options.matchSubset || 1; 
options.matchContains = options.matchContains || 0; 
options.cacheLength = options.cacheLength || 1; 
options.mustMatch = options.mustMatch || 0; 
options.extraParams = options.extraParams || {}; 
options.loadingClass = options.loadingClass || "ac_loading"; 
options.selectFirst = options.selectFirst || false; 
options.selectOnly = options.selectOnly || false; 
options.maxItemsToShow = options.maxItemsToShow || -1; 
options.autoFill = options.autoFill || false; 
options.width = parseInt(options.width, 10) || 0; 
this.each(function() { 
var input = this; 
new jQuery.autocomplete(input, options); 
}); 
// Don't break the chain 
return this; 
} 
jQuery.fn.autocompleteArray = function(data, options) { 
return this.autocomplete(null, options, data); 
} 
jQuery.fn.indexOf = function(e) { 
for (var i = 0; i < this.length; i++) { 
if (this[i] == e) return i; 
} 
return -1; 
};
Javascript 相关文章推荐
javascript 自动填写表单的实现方法
Apr 09 Javascript
jquery+ajax验证不通过也提交表单问题处理
Dec 12 Javascript
JS实现的表格操作类详解(添加,删除,排序,上移,下移)
Dec 22 Javascript
获取阴历(农历)和当前日期的js代码
Feb 15 Javascript
分分钟玩转Vue.js组件
Oct 25 Javascript
Easyui笔记2:实现datagrid多行删除的示例代码
Jan 14 Javascript
vue中本地静态图片路径写法
Mar 06 Javascript
node实现基于token的身份验证
Apr 09 Javascript
KOA+egg.js集成kafka消息队列的示例
Nov 09 Javascript
微信小程序按钮点击跳转页面详解
May 06 Javascript
JS开发 富文本编辑器TinyMCE详解
Jul 19 Javascript
viewer.js实现图片预览功能
Jun 24 Javascript
javaScript call 函数的用法说明
Apr 09 #Javascript
javascript 自动填写表单的实现方法
Apr 09 #Javascript
Extjs入门之动态加载树代码
Apr 09 #Javascript
JS 非图片动态loading效果实现代码
Apr 09 #Javascript
extJs 下拉框联动实现代码
Apr 09 #Javascript
禁止js文件缓存的代码
Apr 09 #Javascript
javascript+mapbar实现地图定位
Apr 09 #Javascript
You might like
10条PHP编程习惯助你找工作
2008/09/29 PHP
PHP校验ISBN码的函数代码
2011/01/17 PHP
ThinkPHP分组下自定义标签库实例
2014/11/01 PHP
PHP设计模式之装饰者模式代码实例
2015/05/11 PHP
给WordPress中的留言加上楼层号的PHP代码实例
2015/12/14 PHP
PHP使用stream_context_create()模拟POST/GET请求的方法
2016/04/02 PHP
基于jquery的代码显示区域自动拉长效果
2011/12/07 Javascript
Dojo获取下拉框的文本和值实例代码
2016/05/27 Javascript
ionic隐藏tabs的方法
2016/08/29 Javascript
JavaScript中const、var和let区别浅析
2016/10/11 Javascript
Vue加载组件、动态加载组件的几种方式
2018/08/31 Javascript
使用 JavaScript 创建并下载文件(模拟点击)
2019/10/25 Javascript
H5实现手机拍照和选择上传功能
2019/12/18 Javascript
Vue中fragment.js使用方法小结
2020/02/17 Javascript
vue键盘事件点击事件加native操作
2020/07/27 Javascript
vant-ui组件调用Dialog弹窗异步关闭操作
2020/11/04 Javascript
[00:55]2015国际邀请赛中国区预选赛5月23日——28日约战上海
2015/05/25 DOTA
Python time模块详解(常用函数实例讲解,非常好)
2014/04/24 Python
分享一下Python数据分析常用的8款工具
2018/04/29 Python
python 读取目录下csv文件并绘制曲线v111的方法
2018/07/06 Python
深入理解HTML的FormData对象
2016/05/17 HTML / CSS
Html5元素及基本语法详解
2016/08/02 HTML / CSS
Oakley官网:运动太阳镜、雪镜和服装
2016/09/30 全球购物
泰国Robinson百货官网:购买知名品牌的商品
2020/02/08 全球购物
Ruby中的保护方法和私有方法与一般面向对象程序设计语言的一样吗
2013/05/01 面试题
最新党员的自我评价分享
2013/11/04 职场文书
暑期实践思想汇报
2014/01/06 职场文书
绿色环保口号
2014/06/12 职场文书
机械工程及自动化专业求职信
2014/09/03 职场文书
谢师宴邀请函
2015/02/02 职场文书
北京天坛导游词
2015/02/12 职场文书
军训阅兵新闻稿
2015/07/17 职场文书
辩论赛新闻稿
2015/07/17 职场文书
Redis主从配置和底层实现原理解析(实战记录)
2021/06/30 Redis
Java中API的使用方法详情
2022/04/06 Java/Android
vue-cli3.0修改打包后的文件名和文件地址,打包后本地运行报错解决
2022/04/06 Vue.js