Blog/Components/Pages/Warframe.razor.js 1.8 K · 54 lines · raw · history

1 // Typeahead for the search box. Rather than ship 3,500 item names to every visitor, the input asks
2 // for the handful matching what has been typed and drops them into its <datalist>.
3 //
4 // Not importing /common.module.js: that module binds to a #log element on load, which this page
5 // does not have.
6
7 const input = document.getElementById("q");
8 const names = document.getElementById("drop-names");
9
10 /** The request whose answer is still wanted; any earlier one is stale. */
11 let inFlight = null;
12
13 if (input && names) {
14 input.addEventListener("input", debounce(suggest, 150));
15
16 // Someone arriving on ?q=… already has a query typed; offer the list for it straight away.
17 if (input.value.trim()) suggest();
18 }
19
20 async function suggest() {
21 const query = input.value.trim();
22 if (query.length < 2) {
23 names.replaceChildren();
24 return;
25 }
26
27 // A slow response for "ni" must not overwrite the suggestions for "nikana p".
28 inFlight?.abort();
29 const request = inFlight = new AbortController();
30
31 try {
32 const response = await fetch(`/api/warframe/names?q=${encodeURIComponent(query)}`, {signal: request.signal});
33 if (!response.ok) return;
34
35 const suggestions = await response.json();
36 names.replaceChildren(...suggestions.map(name => {
37 const option = document.createElement("option");
38 option.value = name;
39 return option;
40 }));
41 } catch (error) {
42 // An abort is this function doing its job; any other failure just means no suggestions,
43 // and the form still submits.
44 if (error.name !== "AbortError") console.debug("suggest failed", error);
45 }
46 }
47
48 function debounce(callback, wait) {
49 let timeoutId = null;
50 return (...args) => {
51 window.clearTimeout(timeoutId);
52 timeoutId = window.setTimeout(() => callback(...args), wait);
53 };
54 }