Created
November 20, 2011 08:02
-
-
Save neesenk/1379951 to your computer and use it in GitHub Desktop.
一个自动补全的类,支多种数据获取方式
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* zhiyongliu <zhiyongliu@tencent.com> | |
* | |
* conf.getData(str, callback) 用来获取数据, 获取的数据通过回调传入, callback传入的参数要是一个数组 | |
* conf.createChild(obj, inputvalue) 用来生成补全的节点, obj为回调callback的数组的某一项, | |
* 在数组为空时obj可能为null,生成出错信息, inputval时当前的input的输入值; | |
* 返回一个dom对象,将会作为一个子节点插入到rootid节点中; | |
* 函数根据需要可能要设置mouseover/mouserout/click的事件处理函数 | |
* conf.toSting(obj) 返回obj对应的字符串, obj为回调callback的数组的某一项 | |
* conf.enterAction(inputvaule) input的回车时的动作可选 | |
* | |
* Autocompleter('testinput', 'testroot', { | |
* toString: function (obj) { return obj; }, | |
* getData: function (str, callback) { callback([str + 1, str + 2, str + 3]); }, | |
* createChild: function (obj, inputval) { | |
* var div = document.createElement("div"); | |
* if (obj) { | |
* div.innerHTML = obj.replace(inputval, '<strong>' + inputval + '</strong>'); | |
* div.onmouseover = function () { this.className = 'red'; }; | |
* div.onmouseout = function () { this.className = '';}; | |
* div.onclick = function () { alert(obj); }; | |
* } else { | |
* div.innerHTML = '没有' + inputval + '相关的内容'; | |
* } | |
* return div; | |
* }, | |
* enterAction: function (inputval) { alert(inputval); } | |
* }); | |
*/ | |
function Autocompleter(inputid, rootid, conf) | |
{ | |
var input = typeof inputid == 'object' ? inputid : document.getElementById(inputid); // input输入框 | |
var rootDiv = typeof rootid == 'object' ? rootid : document.getElementById(rootid); // 补全弹出框的id | |
var stack = []; // 输入补全的缓存 | |
var showStr = ''; // datas 对应的输入 | |
var datas = []; // 当前输入的补全的数据 | |
var childs = []; // 输入框的节点 | |
var origValue = ''; // 当前输入的内容 | |
var trimValue = ''; // 当前输入去除前后空白转换成小写后的结果 | |
var selectIndex = -1; // 通过键盘上下键选择的补全的下标 | |
var inputIndex = -1; // input.value 是datas[inputIndex]补全得来 | |
var pushStack = function (str, data) { stack.push({value: str, data: data}); }; | |
var findStack = function (str) { | |
for (var i = stack.length - 1; i >= 0; i--) { | |
if (stack[i].value === str) | |
return stack[i].data; | |
} | |
return null; | |
}; | |
var addEvent = function (elem, event, callback) { | |
if (elem.addEventListener) | |
elem.addEventListener(event, callback, false); | |
else if (elem.attachEvent) | |
elem.attachEvent("on" + event, callback); | |
}; | |
var trigger = function (elem, event) { | |
if (document.createEvent) { | |
var ev = document.createEvent('MouseEvents'); | |
ev.initEvent(event, false, true); | |
elem.dispatchEvent(ev); | |
} else if (document.createEventObject) { | |
elem.fireEvent('on' + event, document.createEventObject()); | |
} | |
}; | |
var changeSelect = function (pos) { | |
if (selectIndex >= 0 && selectIndex < childs.length) // 触发上一个选择节点的mouseout事件 | |
trigger(childs[selectIndex], 'mouseout'); | |
if (pos >= 0 && pos < childs.length) // 触发当前选择节点的mouseover事件 | |
trigger(childs[pos], 'mouseover'); | |
inputIndex = selectIndex = pos; | |
input.value = (pos >= 0 && pos < datas.length) ? conf.toString(datas[pos]) : origValue; | |
}; | |
var cleanData = function (flag) { | |
rootDiv.innerHTML = "", rootDiv.style.display = 'none'; | |
childs = [], datas = [], inputIndex = -1, selectIndex = -1, showStr = ''; | |
if (flag) | |
origValue = "", trimValue = ""; | |
}; | |
var showData = function(str, data) { | |
var child = null; | |
if (input.value != origValue) | |
input.value = origValue; // 可能不是原始输入的, 需要还原 | |
cleanData(), showStr = str; | |
for (var i = 0; i < data.length; i++) { | |
if (!(child = conf.createChild(data[i], str))) | |
continue; | |
(function (pos) { | |
addEvent(child, 'click', function() { | |
input.value = (pos >= 0 && pos < datas.length) ? conf.toString(datas[pos]) : origValue; | |
cleanData(true); }); | |
addEvent(child, 'mouseover', function () { | |
if (selectIndex >= 0 && selectIndex < childs.length) | |
trigger(childs[selectIndex], 'mouseout'); | |
selectIndex = pos; }); | |
})(i); | |
childs.push(child); | |
datas.push(data[i]); | |
rootDiv.appendChild(child); | |
} | |
if (childs.length === 0 && (child = conf.createChild(null, str))) | |
rootDiv.appendChild(child); | |
rootDiv.style.display = ''; | |
}; | |
var receiveData = function (str, data) { | |
data = data || []; | |
pushStack(str, data); | |
if (showStr != trimValue && str == trimValue) | |
showData(str, data); | |
}; | |
var showComplete = function() { | |
var value = input.value.replace(/^\s+|\s+$/, '').toLowerCase(); | |
var isEq = (inputIndex >= 0 && inputIndex < childs.length) ? | |
(input.value == '' + conf.toString(datas[inputIndex])) : (value == trimValue && trimValue !== ""); | |
if (isEq) { | |
if (rootDiv.style.display === 'none' && rootDiv.innerHTML !== '') | |
rootDiv.style.display = ''; | |
return; | |
} | |
origValue = input.value, trimValue = value; | |
if (value === "") { | |
cleanData(); | |
return; | |
} | |
var data = findStack(value); | |
if (data) | |
showData(value, data); | |
else | |
conf.getData(value, function(data) { receiveData(value, data); }); | |
}; | |
var autoComplete = function (event) { | |
event = event || window.event; | |
showComplete(); | |
if (event.keyCode == 40) // 光标向下移动 | |
changeSelect((selectIndex >= childs.length - 1) ? -1 : selectIndex + 1); | |
else if (event.keyCode == 38)// 光标向上移动 | |
changeSelect((selectIndex <= -1) ? childs.length - 1 : selectIndex - 1); | |
else if (event.keyCode == 13) { // 回车键 | |
if (selectIndex >= 0 && selectIndex < childs.length) | |
trigger(childs[selectIndex], 'click'); | |
else if (conf.enterAction) // input的回车事件 | |
conf.enterAction(input.value); | |
cleanData(true); | |
} else if (event.keyCode == 27) // ESC | |
cleanData(true); | |
}; | |
var trunOffOnclick = function (e) { | |
// 判断b是否是a的子节点 | |
var isChild = function(a,b){return a.contains ? a!=b && a.contains(b) : (a.compareDocumentPosition(b) & 16);}; | |
e = e || window.event; | |
var target = e.target || e.srcElement; // 事件的目标对象 | |
if (!isChild(rootDiv, target) && target != input) | |
rootDiv.style.display = 'none'; | |
}; | |
if (!input || input.tagName != 'INPUT') { | |
alert(inputid + ' is not a input dom'); | |
return; | |
} | |
input.setAttribute('autocomplete', 'off'); // 关闭浏览器的autocomplete | |
addEvent(input, 'keyup', autoComplete); // 键盘事件 | |
addEvent(document, 'click', trunOffOnclick);// 点击其他区域消失 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment