Three things at once
A live search must be fast (don't run on every keystroke — debounce), safe (a menu name from the server must never become HTML) and accessible (screen-reader users hear how many results there are; Escape clears it). Web essentials, all in one small feature.
The search code
function debounce(fn, ms) {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
const input = document.querySelector("#search");
const results = document.querySelector("#results");
const status = document.querySelector("#status");
function show(query) {
const q = query.trim().toLowerCase();
results.replaceChildren();
if (!q) {
status.textContent = "";
return;
}
const found = MENU.filter((name) => name.toLowerCase().includes(q));
for (const name of found) {
const li = document.createElement("li");
li.textContent = name;
results.append(li);
}
status.textContent = found.length ? `${found.length} results` : "No results";
}
input.addEventListener("input", debounce(() => show(input.value), 150));
input.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
input.value = "";
show("");
}
});