Skip to content

Instantly share code, notes, and snippets.

@neesenk
Created November 20, 2011 08:02
Show Gist options
  • Save neesenk/1379951 to your computer and use it in GitHub Desktop.
Save neesenk/1379951 to your computer and use it in GitHub Desktop.
一个自动补全的类,支多种数据获取方式
/**
* 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