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 相关文章推荐
表单元素事件 (Form Element Events)
Jul 17 Javascript
js cookies实现简单统计访问次数
Nov 24 Javascript
js监听滚动条滚动事件使得某个标签内容始终位于同一位置
Jan 24 Javascript
jquery判断小数点两位和自动删除小数两位后的数字
Mar 19 Javascript
JS实现超过长度限制后自动跳转下一款文本框的方法
Feb 23 Javascript
Bootstrap复选框和单选按钮美化插件(推荐)
Nov 23 Javascript
解决Angular.Js与Django标签冲突的方案
Dec 20 Javascript
详解如何在react中搭建d3力导向图
Jan 12 Javascript
vue使用echarts图表的详细方法
Oct 22 Javascript
详解vue中async-await的使用误区
Dec 05 Javascript
js+canvas实现转盘效果(两个版本)
Sep 13 Javascript
JS常用跨域方法实现原理解析
Dec 09 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
全国FM电台频率大全 - 2 天津市
2020/03/11 无线电
PHPMailer 中文使用说明小结
2010/01/22 PHP
php站内搜索并高亮显示关键字的实现代码
2011/12/29 PHP
apache配置虚拟主机的方法详解
2013/06/17 PHP
Yii2使用swiftmailer发送邮件的方法
2016/05/03 PHP
PHP实现的权重算法示例【可用于游戏根据权限来随机物品】
2019/02/15 PHP
浅谈PHP5.6 与 PHP7.0 区别
2019/10/09 PHP
解决tp5在nginx下修改配置访问的问题
2019/10/16 PHP
再谈javascript原型继承
2014/11/10 Javascript
jquery获取及设置outerhtml的方法
2015/03/09 Javascript
jQuery 1.9.1源码分析系列(十)事件系统之主动触发事件和模拟冒泡处理
2015/11/24 Javascript
js改变style样式和css样式的简单实例
2016/06/28 Javascript
JavaScript中Promise的使用详解
2017/02/26 Javascript
vue2实现移动端上传、预览、压缩图片解决拍照旋转问题
2017/04/13 Javascript
微信小程序新手教程之启动页的重要性
2019/03/03 Javascript
微信小程序 slot踩坑的解决
2019/04/01 Javascript
教你完全理解ReentrantLock重入锁
2019/06/03 Javascript
原生js实现二级联动菜单
2019/11/27 Javascript
Vue 中 template 有且只能一个 root的原因解析(源码分析)
2020/04/11 Javascript
一起来了解一下JavaScript的预编译(小结)
2021/03/01 Javascript
[01:27:43]VGJ.S vs TNC Supermajor 败者组 BO3 第三场 6.6
2018/06/07 DOTA
利用Python读取文件的四种不同方法比对
2017/05/18 Python
Python实现删除文件中含“指定内容”的行示例
2017/06/09 Python
Python 利用scrapy爬虫通过短短50行代码下载整站短视频
2018/10/29 Python
Python-while 计算100以内奇数和的方法
2019/06/11 Python
python命令 -u参数用法解析
2019/10/24 Python
python转化excel数字日期为标准日期操作
2020/07/14 Python
如何在Win10系统使用Python3连接Hive
2020/10/15 Python
canvas之万花筒效果的简单实现(推荐)
2016/08/16 HTML / CSS
叙述DBMS对数据控制功能有哪些
2016/06/12 面试题
什么是servlet
2012/05/08 面试题
道歉信范文
2015/05/12 职场文书
MySQL中utf8mb4排序规则示例
2021/08/02 MySQL
Python中itertools库的四个函数介绍
2022/04/06 Python
浅析Python OpenCV三种滤镜效果
2022/04/11 Python
MySQL详解进行JDBC编程与增删改查方法
2022/06/16 MySQL