Notes that are remembered

Build a notes page that keeps its data, and survives damaged data.

What to build

Functions that load, save and draw a list of notes.

Requirements

  • readNotes() returns the saved array of notes from the key notes. If nothing is saved, or if the saved text is damaged or is not an array, it returns an empty array.
  • render() draws the current saved notes in #notes, with one li per note, replacing what was there. Call it when the page loads.
  • Submitting the form trims the text, ignores a blank note, saves the longer list as JSON, empties the box and draws again.
  • The #clear button removes the key notes and draws again.

Constraints

Use the store object from the first script, which has the same methods as localStorage, because storage is blocked in this practice frame. Do not change that script. The functions must have exactly these names.

Example

store.setItem("notes", JSON.stringify(["Buy rice"]));

Why it works

Treating storage as the single source of truth means that the page always shows what would be there after a reload. Stored text can be edited or damaged, so a page that trusts it can break every time it opens, which makes the try and the shape check essential.

Hints

Hint 1

readNotes uses try ... catch around JSON.parse, and checks the result with Array.isArray.

Hint 2

store.getItem gives null when nothing is saved. JSON.parse(null) gives null, so Array.isArray handles that too.

Hint 3

After any change, save the list and call render.

Solution

Show one correct solution (try it yourself first)
<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Notes</title>
</head>
<body>
  <form id="add">
    <label for="note">New note</label>
    <input id="note" autocomplete="off">
    <button>Save</button>
  </form>
  <button id="clear">Clear all</button>
  <ul id="notes"></ul>
  <script>
    // Stands in for localStorage, which is blocked in this practice frame.
    const store = {
      data: new Map([["notes", '["Buy rice"]']]),
      getItem(key) { return this.data.has(key) ? this.data.get(key) : null; },
      setItem(key, value) { this.data.set(key, String(value)); },
      removeItem(key) { this.data.delete(key); },
    };
  </script>
  <script>
    const list = document.querySelector("#notes");
    const input = document.querySelector("#note");

    function readNotes() {
      try {
        const notes = JSON.parse(store.getItem("notes"));
        return Array.isArray(notes) ? notes : [];
      } catch (err) {
        return [];
      }
    }

    function render() {
      list.replaceChildren();
      for (const note of readNotes()) {
        const li = document.createElement("li");
        li.textContent = note;
        list.append(li);
      }
    }

    document.querySelector("#add").addEventListener("submit", (event) => {
      event.preventDefault();
      const text = input.value.trim();
      if (text === "") return;
      store.setItem("notes", JSON.stringify([...readNotes(), text]));
      input.value = "";
      render();
    });

    document.querySelector("#clear").addEventListener("click", () => {
      store.removeItem("notes");
      render();
    });

    render();
  </script>
</body>
</html>

Lesson: Fetching data and saving it →