datalist: improvements - jscancer - Javascript crap (relatively small)
(HTM) git clone git://git.codemadness.org/jscancer
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) README
(DIR) LICENSE
---
(DIR) commit d04e4412eb81de4e4f0eb8542a089fd66a03331a
(DIR) parent 392f4d4e2c582f2132e1d162012157a1d1c655fa
(HTM) Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Thu, 2 Jun 2016 18:50:51 +0200
datalist: improvements
- align dropdown menu better in some cases: for example in a table cell.
- align dropdown menu width with initial input box width (looks nicer).
- separate datalist_filter and datalist_match, only update the results if the
search query has effect (faster).
- on RETURN key and the datalist is already closed use the default action:
useful for form submit (default behaviour).
- improve behaviour on focus and focus lost (onblur).
Diffstat:
M datalist/datalist.css | 1 -
M datalist/datalist.js | 93 +++++++++++++++++++++----------
2 files changed, 63 insertions(+), 31 deletions(-)
---
(DIR) diff --git a/datalist/datalist.css b/datalist/datalist.css
@@ -3,7 +3,6 @@
position: absolute;
overflow: auto;
z-index: 999;
- width: 600px;
padding: 0;
background-color: #fff;
border: 1px solid #33bbff;
(DIR) diff --git a/datalist/datalist.js b/datalist/datalist.js
@@ -7,7 +7,13 @@ function datalist_init(input) {
var cursel = null, items = [], mouse = true, // enable mouse event handling.
dropdown = document.createElement("div");
dropdown.className = "datalist-dropdown";
- dropdown.style.left = String(input.offsetLeft) + "px";
+ var left = 0;
+ for (var c = input; c; c = c.offsetParent) {
+ if (["absolute", "fixed"].indexOf(c.style.position) == -1)
+ left += c.offsetLeft;
+ }
+ dropdown.style.left = String(left) + "px";
+ dropdown.style.minWidth = String(input.clientWidth) + "px";
for (var i = 0, ec = ellist.children; i < ec.length; i++) {
var div = document.createElement("div");
@@ -23,37 +29,54 @@ function datalist_init(input) {
items.push({ el: div, search: ((div.textContent || div.innerText).toLowerCase() || "").split(" ") });
}
- var datalist_match = function(s) {
+ var datalist_filter = function(data, s) {
var matches = [], tok = s.toLowerCase().split(" ");
- for (var i = 0; i < items.length; i++) {
+ for (var i = 0; i < data.length; i++) {
var fc = 0;
for (var k = 0; k < tok.length && fc < tok.length; k++) {
var f = false;
- for (var j = 0; j < items[i].search.length && fc < tok.length && !f; j++)
- for (var l = 0; l < items[i].search.length && !f; l++)
- if (items[i].search[l].indexOf(tok[k]) != -1)
+ for (var j = 0; j < data[i].search.length && fc < tok.length && !f; j++)
+ for (var l = 0; l < data[i].search.length && !f; l++)
+ if (data[i].search[l].indexOf(tok[k]) != -1)
f = true;
if (f)
fc++;
}
/* all tokens (separated by space) must match. */
if (fc == tok.length)
- matches.push(items[i]);
+ matches.push(data[i]);
}
return matches;
- },
- datalist_render = function(m) {
- var p = dropdown.parentNode;
+ };
+ var prevmatches = [];
+ var prevvalue = null;
+ var datalist_match = function(s) {
+ s = s.toLowerCase();
+ if (s === prevvalue)
+ return prevmatches;
+ // if token string is different or string not in previous search: use raw data,
+ // else filter on existing data and no need to sort.
+ if (prevvalue === null || (prevvalue.split(" ").length != s.split(" ").length) ||
+ s.indexOf(prevvalue) == -1)
+ prevmatches = datalist_filter(items, s);
+ else
+ prevmatches = datalist_filter(prevmatches, s);
+ prevvalue = s;
+ return prevmatches;
+ };
+ var datalist_render = function(m) {
var dd = dropdown.cloneNode(false);
for (var i = 0; i < m.length; i++)
dd.appendChild(m[i].el);
- p.replaceChild(dd, dropdown)
+ dropdown.parentNode.replaceChild(dd, dropdown)
dropdown = dd;
- },
- datalist_show = function(status) {
+ };
+ var datalist_visible = false;
+ var datalist_show = function(status) {
+ datalist_visible = status;
dropdown.className = "datalist-dropdown " + (status ? "visible" : "");
- },
- datalist_setsel = function(el) {
+ };
+ var datalist_setsel = function(el) {
if (cursel)
cursel.className = "";
cursel = el;
@@ -66,7 +89,11 @@ function datalist_init(input) {
case 13: // return
if (cursel)
input.value = cursel.textContent || cursel.innerText;
+ if (!datalist_visible)
+ return datalist_show(false);
datalist_show(false);
+ e.stopPropagation();
+ return !!e.preventDefault();
case 27: break; // escape
case 33: // page up.
case 34: // page down.
@@ -109,18 +136,8 @@ function datalist_init(input) {
}
}
}, false);
- input.addEventListener("keyup", function(e) {
- mouse = true;
- switch (e.which) {
- case 13: // return
- case 27: // escape
- datalist_show(false);
- case 33: // page up.
- case 34: // page down.
- case 38: // arrow up
- case 40: // arrow down
- return;
- }
+
+ var onchange = function() {
var m = datalist_match(input.value);
// check if selection is still active in matches.
if (cursel) {
@@ -138,8 +155,7 @@ function datalist_init(input) {
datalist_render(m);
}
datalist_show(!!m.length);
- }, false);
-
+ };
var focuschange = function(e) {
datalist_setsel(null);
if (e.target === input) {
@@ -152,7 +168,24 @@ function datalist_init(input) {
datalist_show(false);
}
};
- document.addEventListener("focus", focuschange, false);
+ input.addEventListener("input", function() {
+ onchange();
+ });
+ input.addEventListener("keyup", function(e) {
+ mouse = true;
+ switch (e.which) {
+ case 13: // return
+ case 27: // escape
+ datalist_show(false);
+ case 33: // page up.
+ case 34: // page down.
+ case 38: // arrow up
+ case 40: // arrow down
+ return;
+ }
+ onchange();
+ }, false);
+ input.addEventListener("focus", focuschange, false);
document.addEventListener("click", focuschange, false);
input.parentNode.insertBefore(dropdown, input.nextSibling);