Lab 9: Forms and Storage
Due: Monday, Apr 21st
In this lab, you will create a tiny app to write greeting cards that you can send to your friends and family!
To get started:
- Download and unzip lab9.zip.
- Open the folder in VS Code.
- Look over the provided code to get a feel for what it does and where you need to add your own code.
Part 1: Card Creator¶
First, you will focus on card-creator.html and createcard.js.
The finished application will look like this screenshot:

Step 1: Creating the Form¶
We've provided the code and styles for the "preview" area of the page, but you'll need to create the form. Provide the functionality for a user to specify what their card's title, message, to, from, etc. should be. Follow the image and the instructions below:

- Create an HTML
forminside theform-area.- Give the form the class
card-form.
- Give the form the class
- You will need four text
inputs and atextarea, each with its ownlabel.- Put each group of
labelplusinputin its owndiv. - Give these
divs a class ofform-group. - Make sure to include
placeholdertext. - Make sure each element has a
nameandid.
- Put each group of
- Add
buttons at the end of theform.- Put them inside a
divwith the classform-buttons. - The preview button should have
type="button". - The save button should have
type="submit". - Make sure they have
ids to access them in JavaScript.
- Put them inside a
Step 2: Adding Functionality¶
Notice the end of the body loads createcard.js.
Open this file and implement the following event listeners:
- When the "preview" button is clicked, you should set the text in all the appropriate
spans to the value of the corresponding inputs in the form. -
When the "save" button is clicked, you should add the card to an array and save the array of cards to
localStorage:- You should start by loading the existing array from
localStoragewith the keycards.- If there doesn't exist an entry with the key, create a new array and assign it to a variable.
- If there does exist an entry with the key, you should parse it and assign it to a variable.
- Create a new JavaScript object (aka map/dictionary) to represent the current card.
- It should have five properties:
to,from,title,subtitle, andmessage. - The properties should be set to the corresponding value from the form.
- It should have five properties:
- Add the new object to the end of the array.
- Store the array in
localStorageto the entry with the keycards, overwriting any existing entry.- Don't forget:
localStorageonly lets you store strings, so you'll need to "stringify" the array!
- Don't forget:
-
Saving multiple cards should result in an array containing all the cards. You can use the developer console in Chrome/Edge and Firefox to check if you're doing this correctly:

Hints
localStorage.setItem("cards", ...)will completely replace the existing item in localStorage (meaning you will override the previous array of cards).- Thus, you need to make sure to load the existing array of cards with
localStorage.getItem("cards"), push a new item to that array, and then save the whole array back to localStorage. - Don't forget that localStorage stores values as strings.
- To convert an array or object to a string:
let a_string = JSON.stringify(an_object); - To convert a string back to an array or object:
let an_object = JSON.parse(a_string);
- To convert an array or object to a string:
- You should start by loading the existing array from
Part 2: Card Viewer¶
Next, you will focus on card-viewer.html and viewcards.js.
The finished application will look like this screenshot:

Previously you made a page that lets users create greeting cards and save them to localStorage. But now we need a way to view the cards, edit them, and delete them (read, update, and delete in the CRUD operations).
Step 3: Loading and Looping¶
First, we will need to load the array of cards from localStorage so that we can loop over and display each card.
- In
viewcards.js, use localStorage to get the item with the key"cards"and convert it back to an array.- Store this to a variable or constant named
cards. - Don't forget to use
JSON.parse(...).
- Store this to a variable or constant named
- Next, use a "traditional"
forloop to loop over the array (using indexes – this will be important later).- Recommended: save
cards[i]to a variable calledcardto make it easier to reference. - Inside the loop, use
console.log(...)to print the current card object in the iteration.
- Recommended: save
- Test your page in the browser and open the developer console.
- FYI, you should always be testing your code like this to make sure there are no JavaScript errors!
-
You should see something like this:

Step 4: Copying the Template¶
The next task you will tackle is to display a card for each item in the array.
In the HTML, you will find a <main> element with the id of card-list.
All the card displays should be a child of this element.
One way to do this is to create all the elements manually in JavaScript (using document.createElement(...)) to form parts of a card, and then add it to the card list.
This approach, however, is more difficult to maintain.
We should be able to define what we want a single card to look like in HTML and CSS and then reuse that code for each card.
The <template> element makes our lives much easier.
It lets us write HTML that doesn't render in the browser, but can be cloned and inserted into our page.
- First, take a look at this article: HTML
<template>Tag- In the article, look at the HTML for the examples. Do you see where the templates are defined?
- Let's apply the same principle to our card viewer:
- In your JavaScript, select the template element and save it to a variable named
template. - Inside the loop, call
template.content.cloneNode(true)to create a copy of the template. Then save the copy to a variable namedcardView. - Finally, select the element with the
card-listclass and call.appendChild(cardView)to add the newly created card to the list.
- In your JavaScript, select the template element and save it to a variable named
-
Test your code. You should have multiple cards displayed, one for each card in localStorage:

Step 5: Showing the Data¶
Now, we need to populate the newly created cardView with data from the current card.
- Put your code before you call
.appendChild(), but after.cloneNode(): - First, select each of the
<span>s that are used to display the card's text and save them to variables:- For example:
let titleText = cardView.querySelector(".title-text"); - You should have five of these, one for each span.
- For example:
- Set each of those
<span>'stextContentto the appropriate values from the current card.- Remember that each card is an object, so you can access the attributes directly:
card.title - The five attributes are:
to,from,title,subtitle,message
- Remember that each card is an object, so you can access the attributes directly:
-
Test your code. Each card should display its corresponding text:

Step 6: Deleting a Card¶
Let's make the delete button work.
- Go to your code inside the loop. Make sure that the following lines are still before you call
.appendChild(): - We need to make the delete button for the current card do something.
- First, select the delete button on the
cardView. - Then, add an event listener of type
"click".
- First, select the delete button on the
- When the button is clicked, we want to delete this card. The problem is, how does each delete button know which card its referring to?
- In the callback function, write
console.log(i)to log the current index. - Test your page and open the developer console. Click on the delete buttons. Notice how each button correctly prints out the corresponding index!
- How does this work? When we create a function, JavaScript will "save" the state of our variables in something called a closure.
- If this isn't working, you may have declared your variables with
varinstead oflet. This is one of the advantages oflet. It scopes the variable to the block instead of the function, which lets us keep its current state.
- In the callback function, write
- Now, in the button's callback function:
- Make the button delete the item in the array at the current index
- Use the
.splice(...)method to remove an item at a given index.
- Use the
- Afterwards, save the array of cards back to localStorage.
- Reload the page by calling
location.reload().- We do this because, after deleting an item, the indexes in the array are no longer valid.
- Refreshing the page will display all the cards again and rebuild the indexes.
- Make the button delete the item in the array at the current index
- Test your code. You should be able to delete a card and, after the page reloads, the deleted card should no longer be displayed.
Step 7: Making the Cards Editable¶
Last but not least, let's make the cards editable.
You may have noticed that there's a contenteditable attribute on each of the <span>s!
This makes the element into something that works both as output and input.
Elements with contenteditable have an event called "input" which is fired whenever a user changes the value of the element.
- Inside the loop, but before
appendChild, let's create a new function.- Name the function
updateCard(). - Inside the function, set each of the current card object's attributes (to, from, title, subtitle, message) to the content from the corresponding
<span>s. - Afterwards, save the entire array to localStorage again.
- Name the function
- After the function definition, but still before
appendChild(), add an event listener for the"input"event to all five<span>s.- Use your
updateCardfunction as the callback.
- Use your
-
Test your code by clicking in one of the fields and editing it. You should see the value change in localStorage:

Submission¶
When you are finished, submit the following files to Gradescope:
card-creator.html, createcard.js, and viewcards.js.
Note this lab will be manually graded.
Going Further¶
Instead of using closures, you can also "store" the index of each card in the HTML elements themselves. You can create a custom HTML attribute, called a data attribute. Here's how that would work instead of closures:
- On each card view and/or each of the
<span>s:- Add a new data attribute called
indexusing JavaScript (aka"data-index"in HTML). - Set the value to the current index of the card.
- Add a new data attribute called
- In your event handlers/callback functions, retrieve the
indexattribute.- Attributes are stored as strings, so you may need to convert it back to an int.
- Use this value to specify which card to delete/update.