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 相关文章推荐
js模拟弹出效果代码修正版
Aug 07 Javascript
jquery在项目中做复选框时遇到的一些问题笔记
Nov 17 Javascript
用原生js做个简单的滑动效果的回到顶部
Oct 15 Javascript
ClearTimeout消除闪动实例代码
Feb 29 Javascript
基于JavaScript实现点击页面任何位置返回
Aug 31 Javascript
dul无法加载bootstrap实现unload table/user恢复
Sep 29 Javascript
20170918 前端开发周报之JS前端开发必看
Sep 18 Javascript
node打造微信个人号机器人的方法示例
Apr 26 Javascript
jQuery内容过滤选择器与子元素过滤选择器用法实例分析
Feb 20 jQuery
深入解析vue 源码目录及构建过程分析
Apr 24 Javascript
Javascript call及apply应用场景及实例
Aug 26 Javascript
javascript实现京东快递单号的查询效果
Nov 30 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
apache配置虚拟主机的方法详解
2013/06/17 PHP
非常好用的Zend Framework分页类
2014/06/25 PHP
ThinkPHP调用common/common.php函数提示错误function undefined的解决方法
2014/08/25 PHP
利用Homestead快速运行一个Laravel项目的方法详解
2017/11/14 PHP
PHP中上传文件打印错误错误类型分析
2019/04/14 PHP
浅析PHP反序列化中过滤函数使用不当导致的对象注入问题
2020/02/15 PHP
javascript 24小时弹出一次的代码(利用cookies)
2009/09/03 Javascript
javascript让setInteval里的函数参数中的this指向特定的对象
2010/01/31 Javascript
js中的异常处理try...catch使用介绍
2013/09/21 Javascript
JavaScript中的console.assert()函数介绍
2014/12/29 Javascript
深入理解JavaScript系列(34):设计模式之命令模式详解
2015/03/03 Javascript
DOM 高级编程
2015/05/06 Javascript
javascript实现查找数组中最大值方法汇总
2016/02/13 Javascript
JS提示:Uncaught SyntaxError: Unexpected token ILLEGAL错误的解决方法
2016/08/19 Javascript
jquery UI Datepicker时间控件冲突问题解决
2016/12/16 Javascript
JavaScript实现大图轮播效果
2017/01/11 Javascript
jQuery NProgress.js加载进度插件的简单使用方法
2018/01/31 jQuery
JavaScript实现一个简易的计算器实例代码
2018/05/10 Javascript
vue.js将时间戳转化为日期格式的实现代码
2018/06/05 Javascript
Vue 实现前端权限控制的示例代码
2019/07/09 Javascript
ES6 Iterator接口和for...of循环用法分析
2019/07/31 Javascript
vue回到顶部监听滚动事件详解
2019/08/02 Javascript
使用Vue-scroller页面input框不能触发滑动的问题及解决方法
2020/08/08 Javascript
python用Pygal如何生成漂亮的SVG图像详解
2017/02/10 Python
Python tkinter 下拉日历控件代码
2020/03/04 Python
通过python 执行 nohup 不生效的解决
2020/04/16 Python
html5的canvas元素使用方法介绍(画矩形、画折线、圆形)
2014/04/14 HTML / CSS
移动端HTML5 input常见问题(小结)
2020/09/28 HTML / CSS
英国最大的体育&时尚零售公司:JD Sports
2017/12/13 全球购物
PUMA澳大利亚官方网站:德国运动品牌
2018/10/19 全球购物
口腔工艺技术专业毕业生自荐信
2013/09/27 职场文书
汉语言文学毕业生自荐信范文
2014/03/24 职场文书
科长竞聘演讲稿
2014/05/16 职场文书
单位介绍信格式
2015/01/31 职场文书
家长必看:义务教育,不得以面试 评测等名义选拔学生
2019/07/09 职场文书
Redis基本数据类型Zset有序集合常用操作
2022/06/01 Redis