datalist: improvements - jscancer - Javascript crap (relatively small) | |
git clone git://git.codemadness.org/jscancer | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit d04e4412eb81de4e4f0eb8542a089fd66a03331a | |
parent 392f4d4e2c582f2132e1d162012157a1d1c655fa | |
Author: Hiltjo Posthuma <[email protected]> | |
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(-) | |
--- | |
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; | |
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 han… | |
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.innerTe… | |
} | |
- 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 && … | |
- for (var l = 0; l < items[i].search.le… | |
- if (items[i].search[l].indexOf… | |
+ for (var j = 0; j < data[i].search.length && f… | |
+ for (var l = 0; l < data[i].search.len… | |
+ if (data[i].search[l].indexOf(… | |
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 sear… | |
+ // else filter on existing data and no need to sort. | |
+ if (prevvalue === null || (prevvalue.split(" ").length != s.sp… | |
+ 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.inn… | |
+ 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); |