add XMLHttprequest support for querying remote results - jscancer - Javascript … | |
git clone git://git.codemadness.org/jscancer | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit 3c60e3f53de3ffe7c696d316ae627a30cdaa384e | |
parent 2e6c151065631ab71a1766c2ec8fc1c61600e5b1 | |
Author: Hiltjo Posthuma <[email protected]> | |
Date: Thu, 29 Jun 2017 18:56:54 +0200 | |
add XMLHttprequest support for querying remote results | |
Refactor some code to be asynchronous, because the async XHR code requires it. | |
This adds 50 lines of code, but it's structured in a way so it can be easily | |
removed: ~185 lines to ~235 LOC. | |
Diffstat: | |
M datalist/README | 11 ++++++----- | |
M datalist/datalist.js | 164 ++++++++++++++++++++---------… | |
A datalist/example-data.json | 13 +++++++++++++ | |
M datalist/example.html | 7 +++++++ | |
4 files changed, 131 insertions(+), 64 deletions(-) | |
--- | |
diff --git a/datalist/README b/datalist/README | |
@@ -1,18 +1,19 @@ | |
-autocomplete | |
-============ | |
+datalist | |
+======== | |
-small dropdown / autocomplete script. | |
+small dropdown filter / autocomplete script. | |
FEATURES | |
-------- | |
- Small: | |
- - Filesize: +- 5.2KB. | |
- - Lines: +- 185, not much code, so hopefully easy to understand. | |
+ - Filesize: +- 6.7KB. | |
+ - Lines: +- 235, not much code, so hopefully easy to understand. | |
- No dependencies on other libraries like jQuery. | |
- (Graceful) fallback to HTML5 datalist if Javascript is disabled. | |
- Filtering values: case-insensitively, tokenized (separated by space). | |
+- Supports querying a remote server for results using a JSON XMLHttpRequest. | |
- Permissive ISC license, see LICENSE file, feel free to contact me for | |
questions or other terms. | |
- Officially supported browsers are: | |
diff --git a/datalist/datalist.js b/datalist/datalist.js | |
@@ -1,16 +1,20 @@ | |
function datalist_init(input) { | |
var attrlist = input.getAttribute("list"), ellist = document.getElemen… | |
- if (attrlist === null || ellist === undefined) | |
- return; | |
+ | |
input.removeAttribute("list"); | |
input.autocomplete = "off"; | |
+ | |
var cursel = null, items = [], mouse = true, // enable mouse event han… | |
- dropdown = document.createElement("div"); | |
+ datalist_match, | |
+ dropdown = document.createElement("div"), | |
+ prevmatches = [], | |
+ prevvalue = null, | |
+ url = input.getAttribute("data-url") || ""; | |
dropdown.className = "datalist-dropdown"; | |
- for (var i = 0, ec = ellist.children; i < ec.length; i++) { | |
+ var createitem = function(s) { | |
var div = document.createElement("div"); | |
- div.innerHTML = ec[i].innerHTML; | |
+ div.innerHTML = s; | |
div.addEventListener("mousedown", function() { | |
input.value = this.textContent || this.innerText; | |
datalist_show(false); | |
@@ -19,44 +23,84 @@ function datalist_init(input) { | |
if (mouse) | |
datalist_setsel(this); | |
}, false); | |
- items.push({ el: div, search: ((div.textContent || div.innerTe… | |
- } | |
+ return { el: div, search: ((div.textContent || div.innerText).… | |
+ }; | |
+ | |
+ if (url.length) { | |
+ // "throttled" JSON XMLHttpRequest. | |
+ var timer = null; | |
+ datalist_match = function(s, fn) { | |
+ clearTimeout(timer); | |
+ timer = setTimeout(function() { | |
+ s = s.toLowerCase(); | |
+ if (s === prevvalue) { | |
+ fn(prevmatches); | |
+ return; | |
+ } | |
+ var x = new(XMLHttpRequest); | |
+ x.onreadystatechange = function() { | |
+ if (x.readyState != 4 || [ 0, 200 ].in… | |
+ return; | |
+ | |
+ prevmatches = []; | |
+ var o = JSON.parse(x.responseText); | |
+ for (var i = 0; i < o.length; i++) | |
+ prevmatches.push(createitem(o[… | |
+ | |
+ prevvalue = s; | |
+ fn(prevmatches); | |
+ }; | |
+ x.open("GET", url + encodeURIComponent(s) + "&… | |
+ x.setRequestHeader("X-Requested-With", "XMLHtt… | |
+ x.timeout = 10000; | |
+ x.send(); | |
+ }, 150); // delay in ms. | |
+ }; | |
+ } else { | |
+ // use inline <datalist>. | |
+ if (attrlist === null || ellist === undefined) | |
+ return; | |
+ for (var i = 0, ec = ellist.children; i < ec.length; i++) | |
+ items.push(createitem(ec[i].innerHTML)); | |
+ | |
+ var datalist_filter = function(data, s) { | |
+ var matches = [], tok = s.toLowerCase().split(" "); | |
+ for (var i = 0; i < data.length; i++) { | |
+ var fc = 0; | |
+ for (var k = 0; k < tok.length && fc < tok.len… | |
+ var f = false; | |
+ for (var j = 0; j < data[i].search.len… | |
+ for (var l = 0; l < data[i].se… | |
+ if (data[i].search[l].… | |
+ f = true; | |
+ if (f) | |
+ fc++; | |
+ } | |
+ // all tokens (separated by space) must match. | |
+ if (fc == tok.length) | |
+ matches.push(data[i]); | |
+ } | |
+ return matches; | |
+ }; | |
- var datalist_filter = function(data, s) { | |
- var matches = [], tok = s.toLowerCase().split(" "); | |
- 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 < 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++; | |
+ datalist_match = function(s, fn) { | |
+ s = s.toLowerCase(); | |
+ if (s === prevvalue) { | |
+ fn(prevmatches); | |
+ return; | |
} | |
- // all tokens (separated by space) must match. | |
- if (fc == tok.length) | |
- matches.push(data[i]); | |
- } | |
- return matches; | |
- }; | |
- 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; | |
- }; | |
+ // if token string is different or string not in previ… | |
+ // else filter on existing data and no need to sort. | |
+ if (prevvalue === null || (prevvalue.split(" ").length… | |
+ s.indexOf(prevvalue) == -1) | |
+ prevmatches = datalist_filter(items, s); | |
+ else | |
+ prevmatches = datalist_filter(prevmatches, s); | |
+ prevvalue = s; | |
+ fn(prevmatches); | |
+ }; | |
+ } | |
+ | |
var datalist_render = function(m) { | |
var dd = dropdown.cloneNode(false); | |
var r = input.getClientRects() || []; | |
@@ -137,20 +181,21 @@ function datalist_init(input) { | |
}, false); | |
var onchange = function() { | |
- var m = datalist_match(input.value); | |
- // check if selection is still active in matches. | |
- if (cursel) { | |
- var hassel = false; | |
- for (var i = 0; i < m.length && !(hassel = (m[i].el ==… | |
- ; | |
- if (!hassel) | |
- datalist_setsel(null); | |
- } | |
- // only one match? select it. | |
- if (m.length == 1) | |
- datalist_setsel(m[0].el); | |
- datalist_render(m); | |
- datalist_show(!!m.length); | |
+ datalist_match(input.value, function(m) { | |
+ // check if selection is still active in matches. | |
+ if (cursel) { | |
+ var hassel = false; | |
+ for (var i = 0; i < m.length && !(hassel = (m[… | |
+ ; | |
+ if (!hassel) | |
+ datalist_setsel(null); | |
+ } | |
+ // only one match? select it. | |
+ if (m.length == 1) | |
+ datalist_setsel(m[0].el); | |
+ datalist_render(m); | |
+ datalist_show(!!m.length); | |
+ }); | |
}; | |
input.addEventListener("input", onchange); | |
input.addEventListener("keyup", function(e) { | |
@@ -169,10 +214,11 @@ function datalist_init(input) { | |
}, false); | |
input.addEventListener("focus", function() { | |
datalist_setsel(null); | |
- var m = datalist_match(input.value); | |
- datalist_render(m); | |
- datalist_show(!!m.length); | |
- dropdown.scrollTop = 0; // reset scroll. | |
+ datalist_match(input.value, function(m) { | |
+ datalist_render(m); | |
+ datalist_show(!!m.length); | |
+ dropdown.scrollTop = 0; // reset scroll. | |
+ }); | |
}, false); | |
input.addEventListener("blur", function() { | |
mouse = true; | |
diff --git a/datalist/example-data.json b/datalist/example-data.json | |
@@ -0,0 +1,12 @@ | |
+[ | |
+"DragonflyBSD", | |
+"GNU/Hurd", | |
+"GNU/Linux", | |
+"FreeBSD", | |
+"MS-DOS 6.11", | |
+"OpenBSD", | |
+"OpenSolaris", | |
+"NetBSD", | |
+"Plan9", | |
+"Windows" | |
+] | |
+\ No newline at end of file | |
diff --git a/datalist/example.html b/datalist/example.html | |
@@ -9,6 +9,8 @@ | |
<form method="post" action=""> | |
+<p>Inline <datalist></p> | |
+ | |
<label for="os">OS: </label> | |
<input type="text" placeholder="Select OS..." value="" list="list" name="os" i… | |
@@ -25,6 +27,11 @@ | |
<option>Windows</option> | |
</datalist> | |
+<p>Using XMLHttpRequest + JSON:</p> | |
+ | |
+<label for="remote">OS: </label> | |
+<input type="text" placeholder="Select OS..." value="" data-url="example-data.… | |
+ | |
</form> | |
<script type="text/javascript" src="datalist.js"></script> |