Everything you've learned
Build a live search box: it waits until you stop typing (performance), shows results safely even if data contains HTML (security), and announces the result count to screen readers (accessibility). A small widget, done professionally.
Three concerns in one widget
A live search box looks simple, but it touches performance, security and accessibility at once — which is why it makes a great capstone. Each concern has a specific technique from this course, and leaving out any one of them produces a widget that's slow, dangerous or unusable for some visitors.
- Performance — debounce the input so filtering runs once after typing pauses, not on every keystroke.
- Security — build result items with createElement and textContent, so a dish named <img onerror=…> shows as text.
- Accessibility — a real <label> for the input, and a live region announcing "3 results" after each search.
- Correctness — trim and lower-case the query; show a friendly message for zero results.
const run = debounce((query) => {
const q = query.trim().toLowerCase();
const matches = DISHES.filter((d) => d.name.toLowerCase().includes(q));
list.replaceChildren(...matches.map((d) => {
const li = document.createElement("li");
li.textContent = d.name;
return li;
}));
status.textContent = `${matches.length} result${matches.length === 1 ? "" : "s"}`;
}, 100);
input.addEventListener("input", () => run(input.value));