291 lines
8.2 KiB
JavaScript
291 lines
8.2 KiB
JavaScript
/*
|
|
By Anders Ytterström 2012.
|
|
|
|
Example use:
|
|
<script src="autocomplete.js"></script>
|
|
<script>
|
|
var values = ["Hansi", "Andre", "The Omen", "Marcus", "Prince Charles"],
|
|
callback = function (k) {
|
|
console.log(k);
|
|
};
|
|
|
|
autocomplete("bg", values, callback);
|
|
</script>
|
|
*/
|
|
|
|
/*jslint indent: 2, maxlen: 90, browser: true */
|
|
var autocomplete = function (id, values, callback) {
|
|
"use strict";
|
|
var autoCompleteTimer,
|
|
stopDefaultAction,
|
|
getPosition,
|
|
field,
|
|
initAutoComplete,
|
|
keydownAutoComplete,
|
|
generateDropdown,
|
|
autoComplete,
|
|
mouseoverDropdown,
|
|
mouseoutDropdown,
|
|
mousedownDropdown,
|
|
assignMouseListeners,
|
|
blurAutoComplete,
|
|
closeDropdown;
|
|
|
|
stopDefaultAction = function (event) {
|
|
event.returnValue = false;
|
|
if (typeof event.preventDefault !== "undefined") {
|
|
event.preventDefault();
|
|
}
|
|
return true;
|
|
};
|
|
|
|
getPosition = function (theElement) {
|
|
var positionX = 0,
|
|
positionY = 0;
|
|
while (theElement !== null) {
|
|
positionX += theElement.offsetLeft;
|
|
positionY += theElement.offsetTop;
|
|
theElement = theElement.offsetParent;
|
|
}
|
|
return [positionX, positionY];
|
|
};
|
|
|
|
initAutoComplete = function () {
|
|
field = document.getElementById(id);
|
|
field.setAttribute("autocomplete", "off");
|
|
if (window.attachEvent) {
|
|
field.attachEvent("onkeydown", keydownAutoComplete);
|
|
} else {
|
|
field.addEventListener("keydown", keydownAutoComplete);
|
|
}
|
|
field.onblur = function () {
|
|
blurAutoComplete();
|
|
};
|
|
};
|
|
|
|
keydownAutoComplete = function (event) {
|
|
var target = event.target || event.srcElement,
|
|
autoCompleteDropdown,
|
|
childLis,
|
|
selected,
|
|
i,
|
|
max,
|
|
inputRanges;
|
|
if (typeof event === "undefined") {
|
|
event = window.event;
|
|
}
|
|
switch (event.keyCode) {
|
|
case 9: // tab
|
|
case 13: // enter
|
|
case 16: // shift
|
|
case 17: // ctrl
|
|
case 18: // alt
|
|
case 20: // caps lock
|
|
case 27: // esc
|
|
case 33: // page up
|
|
case 34: // page down
|
|
case 35: // end
|
|
case 36: // home
|
|
case 37: // left arrow
|
|
case 39: // right arrow
|
|
break;
|
|
case 38: // up arrow
|
|
autoCompleteDropdown = document.getElementById("autoCompleteDropdown");
|
|
if (autoCompleteDropdown !== null) {
|
|
childLis = autoCompleteDropdown.childNodes;
|
|
selected = false;
|
|
for (i = 0, max = childLis.length; i < max; i += 1) {
|
|
if (childLis[i].className === "hover") {
|
|
selected = true;
|
|
if (i > 0) {
|
|
childLis[i].className = "";
|
|
childLis[i - 1].className = "hover";
|
|
target.value = childLis[i - 1].firstChild.nodeValue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!selected) {
|
|
childLis[0].className = "hover";
|
|
target.value = childLis[0].firstChild.nodeValue;
|
|
}
|
|
}
|
|
stopDefaultAction(event);
|
|
break;
|
|
case 40: // down arrow
|
|
autoCompleteDropdown = document.getElementById("autoCompleteDropdown");
|
|
if (autoCompleteDropdown !== null) {
|
|
childLis = autoCompleteDropdown.childNodes;
|
|
selected = false;
|
|
for (i = 0, max = childLis.length; i < max; i += 1) {
|
|
if (childLis[i].className === "hover") {
|
|
selected = true;
|
|
|
|
if (i < childLis.length - 1) {
|
|
childLis[i].className = "";
|
|
childLis[i + 1].className = "hover";
|
|
target.value = childLis[i + 1].firstChild.nodeValue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!selected) {
|
|
childLis[0].className = "hover";
|
|
target.value = childLis[0].firstChild.nodeValue;
|
|
}
|
|
}
|
|
stopDefaultAction(event);
|
|
break;
|
|
case 8: // backspace
|
|
case 46: // delete
|
|
if (typeof autoCompleteTimer !== "undefined") {
|
|
clearTimeout(autoCompleteTimer);
|
|
}
|
|
autoCompleteTimer = setTimeout(function () {
|
|
generateDropdown(false);
|
|
}, 500);
|
|
break;
|
|
default:
|
|
if (typeof autoCompleteTimer !== "undefined") {
|
|
clearTimeout(autoCompleteTimer);
|
|
}
|
|
target = event.target || event.srcElement;
|
|
inputRanges = "false";
|
|
if (typeof target.createTextRange !== "undefined"
|
|
|| typeof target.setSelectionRange !== "undefined") {
|
|
inputRanges = "true";
|
|
}
|
|
autoCompleteTimer = setTimeout(function () {
|
|
generateDropdown(inputRanges);
|
|
}, 500);
|
|
}
|
|
return true;
|
|
};
|
|
|
|
assignMouseListeners = function (e) {
|
|
e.onmouseover = function () {
|
|
mouseoverDropdown(this);
|
|
};
|
|
e.mouseout = function () {
|
|
mouseoutDropdown(this);
|
|
};
|
|
e.mousedown = function () {
|
|
mousedownDropdown(this);
|
|
};
|
|
};
|
|
|
|
generateDropdown = function (doAutoComplete) {
|
|
closeDropdown();
|
|
var input = document.getElementById(id),
|
|
newUl = document.createElement("div"),
|
|
newLi,
|
|
i,
|
|
max = values.length;
|
|
newUl.setAttribute("id", "autoCompleteDropdown");
|
|
newUl.autoCompleteInput = input;
|
|
newUl.style.position = "absolute";
|
|
newUl.style.left = getPosition(input)[0] + "px";
|
|
newUl.style.top = getPosition(input)[1] + input.offsetHeight - 2 + "px";
|
|
newUl.style.width = input.offsetWidth - 3 + "px";
|
|
for (i = 0; i < max; i += 1) {
|
|
if (values[i].indexOf(input.value) === 0) {
|
|
newLi = document.createElement("button");
|
|
newLi.innerHTML = values[i];
|
|
assignMouseListeners(newLi);
|
|
newUl.appendChild(newLi);
|
|
}
|
|
}
|
|
if (newUl.firstChild !== null) {
|
|
document.getElementsByTagName("body")[0].appendChild(newUl);
|
|
}
|
|
if (typeof doAutoComplete !== "undefined" && doAutoComplete) {
|
|
autoComplete();
|
|
}
|
|
return true;
|
|
};
|
|
|
|
autoComplete = function () {
|
|
var input = document.getElementById(id),
|
|
cursorMidway = false,
|
|
range,
|
|
originalValue,
|
|
autoCompleteDropdown;
|
|
if (typeof document.selection !== "undefined") {
|
|
range = document.selection.createRange();
|
|
if (range.move("character", 1) !== 0) {
|
|
cursorMidway = true;
|
|
}
|
|
} else if (typeof input.selectionStart !== "undefined"
|
|
&& input.selectionStart < input.value.length) {
|
|
cursorMidway = true;
|
|
}
|
|
originalValue = input.value;
|
|
autoCompleteDropdown = document.getElementById("autoCompleteDropdown");
|
|
if (autoCompleteDropdown !== null && !cursorMidway) {
|
|
autoCompleteDropdown.firstChild.className = "hover";
|
|
input.value = autoCompleteDropdown.firstChild.firstChild.nodeValue;
|
|
if (typeof input.createTextRange !== "undefined") {
|
|
range = input.createTextRange();
|
|
range.moveStart("character", originalValue.length);
|
|
range.select();
|
|
} else if (typeof input.setSelectionRange !== "undefined") {
|
|
input.setSelectionRange(originalValue.length, input.value.length);
|
|
}
|
|
if (autoCompleteDropdown.childNodes.length === 1) {
|
|
setTimeout(function () {
|
|
closeDropdown();
|
|
}, 10);
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
mouseoverDropdown = function (target) {
|
|
var childLis, i, max;
|
|
while (target.nodeName.toLowerCase() !== "button") {
|
|
target = target.parentNode;
|
|
}
|
|
childLis = target.parentNode.childNodes;
|
|
max = childLis.length;
|
|
for (i = 0; i < max; i += 1) {
|
|
childLis[i].className = "";
|
|
}
|
|
target.className = "hover";
|
|
return true;
|
|
};
|
|
|
|
mouseoutDropdown = function (target) {
|
|
while (target.nodeName.toLowerCase() !== "button") {
|
|
target = target.parentNode;
|
|
}
|
|
target.className = "";
|
|
return true;
|
|
};
|
|
|
|
mousedownDropdown = function (target) {
|
|
while (target.nodeName.toLowerCase() !== "button") {
|
|
target = target.parentNode;
|
|
}
|
|
target.parentNode.autoCompleteInput.value = target.firstChild.nodeValue;
|
|
closeDropdown();
|
|
return true;
|
|
};
|
|
|
|
blurAutoComplete = function () {
|
|
if (typeof autoCompleteTimer !== "undefined") {
|
|
clearTimeout(autoCompleteTimer);
|
|
}
|
|
closeDropdown();
|
|
callback(field.value);
|
|
return true;
|
|
};
|
|
|
|
closeDropdown = function () {
|
|
var autoCompleteDropdown = document.getElementById("autoCompleteDropdown");
|
|
if (autoCompleteDropdown !== null) {
|
|
autoCompleteDropdown.parentNode.removeChild(autoCompleteDropdown);
|
|
}
|
|
return true;
|
|
};
|
|
initAutoComplete();
|
|
};
|