1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| function debounce(callback: (...args: unknown[]) => void, wait: number) { let timeoutId = 0; return (...args: unknown[]) => { if (timeoutId !== 0) { window.clearTimeout(timeoutId); } timeoutId = window.setTimeout(() => { callback(null, ...args); }, wait); }; }
function autocomplete(el: HTMLInputElement, url: string, field: string, title: (x: unknown) => string, val: (x: unknown) => string) { if (!el) { return; } const listId = el.id + "-list"; const list = document.createElement("datalist");
const loadingOpt = document.createElement("option"); loadingOpt.value = ""; loadingOpt.innerText = "Loading..."; list.appendChild(loadingOpt);
list.id = listId; el.parentElement?.prepend(list);
el.setAttribute("autocomplete", "off"); el.setAttribute("list", listId);
const cache: { [_: string]: { url: string; complete: boolean; data: unknown[]; frag: DocumentFragment; } } = {}; let lastQuery = "";
function getURL(q: string): string { const dest = url; if (dest.includes("?")) { return dest + "&t=json&" + field + "=" + encodeURIComponent(q); } return dest + "?t=json&" + field + "=" + encodeURIComponent(q); }
function datalistUpdate(q: string) { const c = cache[q]; if (!c || !c.frag) { return; } lastQuery = q; list.replaceChildren(c.frag.cloneNode(true)); }
function f() { const q = el.value; if (q.length === 0) { return; } const dest = getURL(q); let proceed: boolean = !q || !lastQuery; if (!proceed) { const l = cache[lastQuery]; if (l) { proceed = !l.data.find((d) => q === val(d)); } } if (!proceed) { return; } if (cache[q] && cache[q].url === dest) { datalistUpdate(q); return; }
fetch(dest, {credentials: "include"}).then((res) => res.json()).then((data) => { if (!data) { return; } const arr = Array.isArray(data) ? data : [data]; const frag = document.createDocumentFragment(); let optMax = 10; for (let d = 0; d < arr.length && optMax > 0; d++) { const v = val(arr[d]); const t = title(arr[d]); if (v) { const option = document.createElement("option"); option.value = v; option.innerText = t; frag.appendChild(option); optMax--; } } cache[q] = {url: dest, data: arr, frag: frag, complete: false}; datalistUpdate(q); }); }
el.oninput = debounce(f, 250); console.log("managing [" + el.id + "] autocomplete"); }
export function autocompleteInit() { return autocomplete; }
|