JavaScript · Module 10
Remember small choices and finish the event finder
Separate temporary state from saved state
State means the information the program currently uses: loaded events, search text, selected category and saved event IDs. Most state can live in variables while the page is open. Reloading loses those variables. We want only the saved IDs to survive a reload, so we will write a small JSON array into localStorage. We will not store names, email, passwords, tokens or registration records.
Local storage belongs to an origin, which includes the scheme, host and port. Changing from 127.0.0.1 to localhost, or from port 8000 to 8001, gives a different origin and therefore a different storage area. A preference saved on one does not automatically appear on the other. Local storage is not an account, a cloud backup or synchronization between devices. A browser may clear or deny it.
Serialize and parse deliberately
Storage values are strings. JSON.stringify converts our array into JSON text; JSON.parse converts JSON text back into a JavaScript value. Neither operation validates our business rules. A stored value can be old, corrupted or edited in Developer Tools. Treat it as input.
const key = "harbor-together.saved.v1";
const ids = ["garden-morning"];
localStorage.setItem(key, JSON.stringify(ids));
const text = localStorage.getItem(key);
const restored = text === null ? [] : JSON.parse(text);
The snippet above demonstrates serialization only; use the guarded version below when storage can fail.
let ids = [];
try {
const raw = localStorage.getItem("harbor-together.saved.v1");
const parsed = raw === null ? [] : JSON.parse(raw);
if (!Array.isArray(parsed) || !parsed.every((id) => typeof id === "string")) {
throw new Error("Invalid saved IDs");
}
ids = parsed;
} catch (error) {
console.warn("Using an empty in-memory shortlist.");
}
The catch keeps ids as an empty array, allowing the page to continue. The capstone also displays a warning and removes unknown or duplicate IDs. The v1 suffix identifies the data format, not a security mechanism. getItem returns null if the key is absent. A value of "[]" represents a present but empty saved list. Parse failures must be caught. So must storage access and writes: policy restrictions or capacity limits may throw. A usable event finder should still work for this page session when persistence is unavailable.
Keep the failure boundary small
Our provided storage module wraps access, parsing and writing in try/catch. It verifies that restored data is an array of strings and removes duplicates. The app reconciles IDs against the full normal catalog only after events.json loads successfully; an empty test fixture must not erase an existing shortlist. Unknown IDs are then ignored because their events may have been removed. If reading fails, it returns an empty in-memory list with a warning. If writing fails, it keeps the current in-memory choice and tells the visitor that a reload may lose it.
Use removeItem(key) for a “Clear saved events” action. Do not call localStorage.clear(): other tools on the same origin may use unrelated keys. A storage error should never be reported as a successful permanent save. Also avoid interpreting “saved” as “reserved.” A saved event is just this browser's shortlist.
Follow one complete user action
Start with saved IDs empty. The visitor activates the Garden morning save button. The program checks whether garden-morning is present. It is not, so a new array contains that ID. The program tries to persist the array. It keeps the existing button and its label stable, updates its aria-pressed state, and announces the result separately. If the visitor activates it again, filtering out the ID produces an empty array.
When “saved only” is active, removing a card from the list can also remove the focused button. Our reference moves focus back to the saved-only control before removing that focused card. It does not steal focus when a normal category filter changes. These are small details, but they make the interaction more predictable for keyboard users.
Assemble the capstone in checkpoints
Read the capstone brief before opening its solution. Begin with the starter's static markup and supplied data. First implement a pure title/category filter and test it. Next render cards using createElement and textContent. Then attach controls once and show a count plus an empty state. Add the synthetic-only form only after filtering works. Finally add the loader and saved-ID storage. Each checkpoint should have evidence before you move to the next.
The reference application separates core data rules, storage helpers and UI work into modules. You are not required to memorize its layout. The goal is to explain one action from input through data transformation to display, and to show what happens when its assumptions fail. If a function feels mysterious, return to the lesson that introduced its operation and write a smaller test.
What completion actually means
A working happy path is necessary but incomplete. Try no matches, a full event, invalid fields, a missing JSON file, corrupt saved data and denied storage. Use the keyboard. Resize the viewport and inspect focus visibility. Turn JavaScript off and confirm the fallback still explains the practice nature of the page. Do not claim a registration occurred or that data was sent anywhere.
Automated checks in the package cover selected data and browser behaviors. They cannot demonstrate that a beginner understands the material or that every assistive technology works. The release report states which environments were actually checked and which checks remain. A human pilot and final website integration are separate acceptance steps.
Carry the habits forward
You have used values, branches, loops, functions and collections to express rules; DOM and events to connect those rules to a page; modules and tests to organize evidence; promises to handle waiting; and local storage to remember a bounded choice. The most reusable habit is making a small claim, testing it and explaining the result. Your next project can be different while keeping that same discipline.
References: localStorage, WHATWG Web Storage.
Practice and next step
Open exercise 10. Attempt the prediction before running code. Record the observed result and explain any mismatch. The explained solution is separate so you can choose when to look.
Read the separate answers and explanations
Reading progress
10 lessons remain open to guests. Marking a lesson read records reading only; it does not award assessment credit or a certificate.
Device reading marks require browser storage. Reading is always available.
Sign in or create an account to save separate account progress. Your current page is kept.
Your earlier place on this device suggests these lessons. No new lesson is marked read.