Web Storage API - localStorage
Overview
There are times when non-sensitive data storage would provide a better user experience and offer some site performance improvements. The Web Storage API mechanisms provide the ability to store information in a key-value format based upon a particular user agent or origin. In this activity, we discover the purpose and scope of the localStorage mechanism available through the Storage interface. We will use this knowledge to drive and display some traffic data from individual clients on a page.
The web storage data is actually saved in a SQLite file in a subfolder or file in the user's profile folder.
Prepare
Here are some comparison data between cookies, localStorage, and sessionStorage.
Cookies | localStorage | sessionStorage | |
---|---|---|---|
Capacity | ~4KB | ~5MB | ~5MB |
Expires | Manually set | Never | On tab/windows close |
Accessible from | Any window | Any Window | Same tab |
Each of these methods stores data with the actual user agent (browser) client and not on the server.
localStorage Advantages
- This method is much more intuitive than using the older method of Cookies.
- Only stores strings in key-value pairs.
- The mechanism's data persists without expiring and is available even when an agent is closed and reopened again.
You can store non-string data in localStorage. One common approach is to use the
JSON.stringify()
method to convert the data to a string before storing it, and
then use the JSON.parse()
method to convert it back to its original form when
you
retrieve it from localStorage. This allows you to store and retrieve more complex data
structures, such as objects and arrays, in localStorage.
localStorage Demonstration
- Watch: localStorage
Demonstration - Here is the corresponding CodePen used in demonstration.
Fork (Copy) this Pen to your own CodePen account and play with the code.
What are some of the differences between session and local storage?
One main difference is that localStorage data is persistent (remains) even when the browser session expires. Session variables do not.
Using the Storage interface, what are some methods built into this interface object and what do they do?
Out of the entire method list [key(), getItem(), setItem(), removeItem(),
clear()] the most common are getItem() and setItem() which
you will be using in the assignments.
Storage
Interface of the Web Storage API
Activity Overview
This activity revisits the favorite Book of Mormon chapters exercise. The chapters entered by the user do not persist between visits to the application. In other words, they are not stored anywhere. In this activity, we will use localStorage to store the BOM chapter list for the user.
The high level directions for this problem would be "Enhance the favorite Book of Mormon chapters application so that the users entries persist between visits to the application using the localStorage API". Take on the challenge by first thinking about a solution to this problem and map it out on paper.
The directed plan is to create an array of valid book and chapter entries made by the user. Then that array could be stored in localStorage as one large string using JSON string using JSON.stringify(). This means we need to load that array upon application load with the parsed data from localStorage, IF the named localStorage component exists. Once loaded, we need to populate the list with the stored values.
So instead of having two functions that do the same thing for the most part, we will create a single function that appends the favorite chapter list with the corresponding delete button, once on load, and also when a new entry is made.
Activity Instructions
- Make a copy of your BOM application, by copying the HTML, CSS, and JavaScript from the previous learning activity into a week05 folder.
- Open the JavaScript file. Declare an array named
chaptersArray
and assign it to the results of a defined function namedgetChapterList
(This function does not exist, yet). - In that same array variable declaration and assignment, add a compound OR condition to
assign it an empty array in case this is the user's first visit or if the localStorage item
is
missing.
This works because the function might not return anything, so it is falsy which means it will revert to assigning the empty array to
chaptersArray
.Example
const input = document.querySelector('#favchap'); const button = document.querySelector('button'); const list = document.querySelector('#list'); let chaptersArray = getChapterList() || [];
Code Explanations
The first three lines establish references to the DOM elements that we will be using in the program. Note that they only reference the HTML element objects, not any properties.
The array declaration initializes the chaptersArray variable with the list of chapters returned by the getChapterList() function or an empty array if the function call returns null or undefined.
- Now let's populate the displayed list of chapters. Use a
forEach
on thechaptersArray
to process each entry namedchapter
. Use an arrow function within the loop to call a new defined function nameddisplayList
and pass it the argument ofchapter
. That way each entry will be processed, i.e., appended to the list.Example
chaptersArray.forEach(chapter => { displayList(chapter); });
- Change the button click event listener to only do the following tasks (the other tasks in
that original function will be used in a separate function named
displayList
):- check if the input is empty, if not, then
- call
displayList
with theinput.value
argument, - push the
input.value
into thechaptersArray
, - update the localStorage with the new array by calling a function named
setChapterList
, - set the
input.value
to nothing, and - set the focus back to the input.
Example with // code comments
button.addEventListener('click', () => { if (input.value != '') { // make sure the input is not empty displayList(input.value); // call the function that outputs the submitted chapter chaptersArray.push(input.value); // add the chapter to the array setChapterList(); // update the localStorage with the new array input.value = ''; // clear the input input.focus(); // set the focus back to the input } });
- Create the
displayList
defined function that receives one parameter nameditem
. - Put all the code that builds a list item from the previous button click event listener
into this new
displayList
function and use theitem
parameter as the input. AdeleteChapter
function will need to be called within the delete button click event to remove the chapter from the array and localStorage.Example:
displayList()
function displayList(item) { let li = document.createElement('li'); let deletebutton = document.createElement('button'); li.textContent = item; // note the use of the displayList parameter 'item' deletebutton.textContent = '❌'; deletebutton.classList.add('delete'); // this references the CSS rule .delete{width:fit-content;} to size the delete button li.append(deletebutton); list.append(li); deletebutton.addEventListener('click', function () { list.removeChild(li); deleteChapter(li.textContent); // note this new function that is needed to remove the chapter from the array and localStorage. input.focus(); // set the focus back to the input }); console.log('I like to copy code instead of typing it out myself and trying to understand it.'); }
- Define the
setChapterList
function to set the localStorage item that you have already named. UseJSON.stringify()
to stringify the array.Example:
setChapterList()
function setChapterList() { localStorage.setItem('myFavBOMList', JSON.stringify(chaptersArray)); }
- Define the
getChapterList
function to get the localStorage item. No parameter is needed. Since this function returns to an awaiting array, we will need to useJSON.parse
on the string.Example:
getChapterList()
function getChapterList() { return JSON.parse(localStorage.getItem('myFavBOMList')); }
- Finally, define the
deleteChapter
function with a parameter namedchapter
that does three things.- reformat the
chapter
parameter to get rid of the ❌ that is passed on the end of the chapter string when we called the deleteChapter function. Use string.slice() method to extract the last character.chapter = chapter.slice(0, chapter.length - 1); // this slices off the last character
- redefine the
chaptersArray
array using the array.filter method to return everything except the chapter to be removed.chaptersArray = chaptersArray.filter((item) => item !== chapter);
- Call the
setChapterList
function to update the localStorage item.
Example:
deleteChapter()
function deleteChapter(chapter) { chapter = chapter.slice(0, chapter.length - 1); chaptersArray = chaptersArray.filter(item => item !== chapter); setChapterList(); }
- reformat the
Testing
Test the application by adding and removing chapters. Hard refresh and empty the cache the page and see if the chapters persist. You can also erase all application localStorage content under the Applications tab in DevTools.
Optional Resources
- Video: JavaScript Cookies vs localStorage vs Session Storage - Web Dev Simplified
- A dynamic structured approach to this activity would be to use Set object in JavaScript. Set allows us to add and delete items from the Set object as needed.
- Reference: Web Storage API - MDN
- The IndexedDB API is a more robust method of storing data on the client side and is more like a database.
- The Cache API is a method of storing data that is specifically used for caching data for offline use.