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 相关文章推荐
xheditor与validate插件冲突的解决方案
Apr 15 Javascript
js 小数取整的函数
May 10 Javascript
Js 时间间隔计算的函数(间隔天数)
Nov 15 Javascript
javascript中如何处理引号编码&amp;#034;
Aug 15 Javascript
JQuery获取与设置HTML元素的内容或文本的实现代码
Jun 20 Javascript
JQuery设置获取下拉菜单某个选项的值(比较全)
Aug 05 Javascript
Angular页面间切换及传值的4种方法
Nov 04 Javascript
vue2.0 路由不显示router-view的解决方法
Mar 06 Javascript
js中arguments对象的深入理解
May 14 Javascript
详解基于 Node.js 的轻量级云函数功能实现
Jul 08 Javascript
jQuery实时统计输入框字数及限制
Jun 24 jQuery
vue 使用localstorage实现面包屑的操作
Nov 16 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
SONY ICF-F10中波修复记
2021/03/02 无线电
php从数组中随机选择若干不重复元素的方法
2015/03/14 PHP
Laravel5.1数据库连接、创建数据库、创建model及创建控制器的方法
2016/03/29 PHP
浅谈使用 Yii2 AssetBundle 中 $publishOptions 的正确姿势
2017/11/08 PHP
jquery实现简单的自动播放幻灯片效果
2015/06/13 Javascript
js的flv视频播放器插件使用方法
2015/06/23 Javascript
JS实现slide文字框缩放伸展效果代码
2015/11/05 Javascript
基于jQuery实现简单的折叠菜单效果
2015/11/23 Javascript
jquery easyui datagrid实现增加,修改,删除方法总结
2016/05/25 Javascript
微信小程序 数据封装,参数传值等经验分享
2017/01/09 Javascript
JavaScript设计模式之代理模式详解
2017/06/09 Javascript
Nuxt.js开启SSR渲染的教程详解
2018/11/30 Javascript
[42:20]2014 DOTA2华西杯精英邀请赛5 24 DK VS NewBee
2014/05/25 DOTA
Python调用命令行进度条的方法
2015/05/05 Python
python实现应用程序在右键菜单中添加打开方式功能
2017/01/09 Python
Python机器学习logistic回归代码解析
2018/01/17 Python
Django 连接sql server数据库的方法
2018/06/30 Python
python三引号输出方法
2019/02/27 Python
django之使用celery-把耗时程序放到celery里面执行的方法
2019/07/12 Python
使用Pyinstaller转换.py文件为.exe可执行程序过程详解
2019/08/06 Python
Python使用百度api做人脸对比的方法
2019/08/28 Python
python3实现elasticsearch批量更新数据
2019/12/03 Python
Python读取分割压缩TXT文本文件实例
2020/02/14 Python
在django中使用post方法时,需要增加csrftoken的例子
2020/03/13 Python
CSS3实现歌词进度文字颜色填充变化动态效果的思路详解
2020/06/02 HTML / CSS
input元素的url类型和email类型简介
2012/07/11 HTML / CSS
欧舒丹比利时官网:L’OCCITANE比利时
2017/04/25 全球购物
荷兰领先的百货商店:De Bijenkorf
2018/10/17 全球购物
英国领先的互联网葡萄酒礼品商:Vintage Wine & Port
2019/05/24 全球购物
卫生院健康教育实施方案
2014/06/07 职场文书
运动会宣传口号
2014/06/09 职场文书
2014年教师节红领巾广播稿
2014/09/10 职场文书
公安四风对照检查材料思想汇报
2014/10/11 职场文书
详解Python魔法方法之描述符类
2021/05/26 Python
SpringAop日志找不到方法的处理
2021/06/21 Java/Android
解决Oracle数据库用户密码过期
2022/05/11 Oracle