Skip to content

Prep 6: Introducing Persistence

(But first, some final little pieces about forms.)

Textbook++ Reading

  • HTML/CSS
    • 5.5 Table and Form Accessibility
    • 5.6 Styling and Designing Forms
  • JS

Grocery List with Persistence

This week, we are building a grocery list app from scratch using a starter page that already has the table and styling in place. Your job is to add the form, write the JavaScript to add items, and then save/load the list using localStorage so it persists across reloads.

Getting Started

First, please download and extract the prep06.zip starter code into a prep06 folder somewhere safe (like your cs343 folder). The starter contains the basic page structure and a styled grocery table, but it does not yet include the form or the JavaScript for adding items.

Open the three files in VS Code and preview grocery.html in the browser.

The starter page already includes a data table similar to this:

<table>
  <thead>
    <tr>
      <th scope="col">Item</th>
      <th scope="col">Quantity</th>
      <th scope="col">Category</th>
      <th scope="col">Notes</th>
    </tr>
  </thead>
  <tbody id="groceryList"></tbody>
</table>

Your goal is to complete the rest of the app: build the form, wire it into JavaScript, style it to match the example, and persist the list to localStorage.

Part 1: Build the Grocery Form

Your starter already includes the table, but the form still needs to be created. Use the textbook examples as a reference for how a basic form should be laid out.

  1. In grocery.html, add a form with labels and controls for:
    • item name
    • quantity
    • category
    • notes
  2. Include a submit button.
  3. Give each form control a sensible id (so its <label for="..."> can point to it) and a name, such as:

    Control id name
    Item itemInput item
    Quantity quantityInput quantity
    Category categorySelect category
    Notes notesInput notes
  4. The category field should be a <select> with the following options:

    • Produce
    • Dairy
    • Poultry
    • Meat
    • Pantry
    • Other
  5. The notes field should be a <textarea> with rows="4".
  6. In grocery-script.js, write the JavaScript needed to:
    • read the values from the form, using whichever approach you prefer:
      • FormData: new FormData(formElement) collects every control’s value at once, and formData.get("item") looks one up by its name attribute.
      • Individual controls: look up each control by its id (e.g., document.getElementById("itemInput")) and read its .value property.
    • create a new row in the table
    • add the four cells: item, quantity, category, and notes
    • append the row to the table body
    • reset the form after submission
  7. Test the form before moving on. Add a few items and make sure they appear in the table.

Before any styling, your page should look something like this:

Grocery list with an unstyled form below the table

This part is intentionally more open-ended than the previous prep. You are expected to decide on the structure and names for your JavaScript and HTML elements, as long as the app behaves correctly and the code stays organized.

Part 2: Style the Form

The form should look clean and match the styling patterns from the textbook. The table is already styled for you; now you need to make the form look intentional and readable.

  1. In grocery-styles.css, set the form container width to fit-content and give it 5px vertical padding and 10px horizontal padding.
  2. Create a CSS rule that selects all of the form controls except the submit button, including:
    • the item input
    • the quantity input
    • the category selector
    • the notes textarea
  3. In that rule, do the following:
    • Add the properties to style them like the inputs in Figure 5.31 from the textbook
      • Including the bottom border too! (but not the placeholder)
      • See the image below for a reference
    • In addition, add a declaration to set the width to 300px so that they are all the same width.
    • But wait! They’re not the same width because of how the box model works.
    • Add another declaration to set box-sizing: border-box; to fix this issue.
  4. Style the submit button to look like the “Click” button in Figure 5.31 from the textbook.
  5. Style the form’s labels so that they match the “Vertical” layout in Figure 5.32 from the textbook.
  6. Preview your page and make sure the form looks neat and aligned.

At this stage, the exact CSS names and selectors are up to you, as long as the end result is visually consistent and easy to use. When you’re done, your page should look something like this:

Grocery list with a styled form, labels stacked above each field

Part 3: Saving and Loading from localStorage

Right now, refreshing the page clears out the table. We want the grocery list to persist using localStorage so the items remain after a browser refresh.

localStorage view in Chrome/Edge DevTools

(Note: the above image is from Chrome/Edge DevTools. Firefox uses a “Storage” tab instead of “Application”.)

In grocery-script.js, implement the following pieces:

  1. appendToSavedList()
    • Read the saved list from localStorage if it already exists.
    • The list is stored as a string, using the key "items".
    • You will need to parse it with JSON.parse(...) before using it.
    • If the data does not exist yet, start with an empty array.
    • Add the new item to the array.
    • Save the updated array back to localStorage with JSON.stringify(...).
    • Remember: everything in localStorage is stored as a string.
  2. loadList()
    • Check whether the key "items" exists in localStorage.
    • If it does not exist, do nothing.
    • If it does exist, parse the array and add each item back into the table.
    • Use the same table-building logic you used when adding an item manually.
  3. Call loadList() when the page loads. Either of these approaches works:
    • Add a DOMContentLoaded listener. This event fires once the browser has read the entire HTML document, so it works no matter where the <script> tag is placed.
    • Or just call loadList() directly at the bottom of the script. The browser runs a script as soon as it reaches the <script> tag. Since our <script> tag is at the very end of the <body>, the table has already been built by then, so loadList() can find #groceryList. (If the script were in the <head>, the table wouldn’t exist yet, and this would fail!)
  4. Make sure you save the list after each item is added.
  5. Test the app by adding items, refreshing the page, and confirming the list still appears.

This is the main persistence step of the prep. The starter file includes TODO comments for the localStorage work, so use those as your guide while you write the code.

If you still have any sample rows in the HTML, remove them before you test the final app so the list comes only from user input and localStorage.

Submission

Submit your three files (grocery.html, grocery-styles.css, and grocery-script.js) to Gradescope.